Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libfido2/src/hid_win.c
39478 views
1
/*
2
* Copyright (c) 2019-2022 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
10
#include <fcntl.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 <devpkey.h>
18
#include <devpropdef.h>
19
#include <hidclass.h>
20
#include <hidsdi.h>
21
#include <wchar.h>
22
23
#include "fido.h"
24
25
#if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 6
26
WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO,
27
PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE,
28
DWORD, PDWORD, DWORD);
29
#endif
30
31
#if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 8
32
DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97,
33
0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);
34
#endif
35
36
struct hid_win {
37
HANDLE dev;
38
OVERLAPPED overlap;
39
int report_pending;
40
size_t report_in_len;
41
size_t report_out_len;
42
unsigned char report[1 + CTAP_MAX_REPORT_LEN];
43
};
44
45
static bool
46
is_fido(HANDLE dev)
47
{
48
PHIDP_PREPARSED_DATA data = NULL;
49
HIDP_CAPS caps;
50
int fido = 0;
51
52
if (HidD_GetPreparsedData(dev, &data) == false) {
53
fido_log_debug("%s: HidD_GetPreparsedData", __func__);
54
goto fail;
55
}
56
57
if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
58
fido_log_debug("%s: HidP_GetCaps", __func__);
59
goto fail;
60
}
61
62
fido = (uint16_t)caps.UsagePage == 0xf1d0;
63
fail:
64
if (data != NULL)
65
HidD_FreePreparsedData(data);
66
67
return (fido);
68
}
69
70
static int
71
get_report_len(HANDLE dev, int dir, size_t *report_len)
72
{
73
PHIDP_PREPARSED_DATA data = NULL;
74
HIDP_CAPS caps;
75
USHORT v;
76
int ok = -1;
77
78
if (HidD_GetPreparsedData(dev, &data) == false) {
79
fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir);
80
goto fail;
81
}
82
83
if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
84
fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir);
85
goto fail;
86
}
87
88
if (dir == 0)
89
v = caps.InputReportByteLength;
90
else
91
v = caps.OutputReportByteLength;
92
93
if ((*report_len = (size_t)v) == 0) {
94
fido_log_debug("%s: report_len == 0", __func__);
95
goto fail;
96
}
97
98
ok = 0;
99
fail:
100
if (data != NULL)
101
HidD_FreePreparsedData(data);
102
103
return (ok);
104
}
105
106
static int
107
get_id(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
108
{
109
HIDD_ATTRIBUTES attr;
110
111
attr.Size = sizeof(attr);
112
113
if (HidD_GetAttributes(dev, &attr) == false) {
114
fido_log_debug("%s: HidD_GetAttributes", __func__);
115
return (-1);
116
}
117
118
*vendor_id = (int16_t)attr.VendorID;
119
*product_id = (int16_t)attr.ProductID;
120
121
return (0);
122
}
123
124
static int
125
get_manufacturer(HANDLE dev, char **manufacturer)
126
{
127
wchar_t buf[512];
128
int utf8_len;
129
int ok = -1;
130
131
*manufacturer = NULL;
132
133
if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
134
fido_log_debug("%s: HidD_GetManufacturerString", __func__);
135
goto fail;
136
}
137
138
if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
139
-1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
140
fido_log_debug("%s: WideCharToMultiByte", __func__);
141
goto fail;
142
}
143
144
if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) {
145
fido_log_debug("%s: malloc", __func__);
146
goto fail;
147
}
148
149
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
150
*manufacturer, utf8_len, NULL, NULL) != utf8_len) {
151
fido_log_debug("%s: WideCharToMultiByte", __func__);
152
goto fail;
153
}
154
155
ok = 0;
156
fail:
157
if (ok < 0) {
158
free(*manufacturer);
159
*manufacturer = NULL;
160
}
161
162
return (ok);
163
}
164
165
static int
166
get_product(HANDLE dev, char **product)
167
{
168
wchar_t buf[512];
169
int utf8_len;
170
int ok = -1;
171
172
*product = NULL;
173
174
if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
175
fido_log_debug("%s: HidD_GetProductString", __func__);
176
goto fail;
177
}
178
179
if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
180
-1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
181
fido_log_debug("%s: WideCharToMultiByte", __func__);
182
goto fail;
183
}
184
185
if ((*product = malloc((size_t)utf8_len)) == NULL) {
186
fido_log_debug("%s: malloc", __func__);
187
goto fail;
188
}
189
190
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
191
*product, utf8_len, NULL, NULL) != utf8_len) {
192
fido_log_debug("%s: WideCharToMultiByte", __func__);
193
goto fail;
194
}
195
196
ok = 0;
197
fail:
198
if (ok < 0) {
199
free(*product);
200
*product = NULL;
201
}
202
203
return (ok);
204
}
205
206
static char *
207
get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata)
208
{
209
SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
210
char *path = NULL;
211
DWORD len = 0;
212
213
/*
214
* "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail
215
* with a NULL DeviceInterfaceDetailData pointer, a
216
* DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize
217
* variable. In response to such a call, this function returns the
218
* required buffer size at RequiredSize and fails with GetLastError
219
* returning ERROR_INSUFFICIENT_BUFFER."
220
*/
221
if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len,
222
NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
223
fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
224
__func__);
225
goto fail;
226
}
227
228
if ((ifdetail = malloc(len)) == NULL) {
229
fido_log_debug("%s: malloc", __func__);
230
goto fail;
231
}
232
233
ifdetail->cbSize = sizeof(*ifdetail);
234
235
if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len,
236
NULL, NULL) == false) {
237
fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
238
__func__);
239
goto fail;
240
}
241
242
if ((path = strdup(ifdetail->DevicePath)) == NULL) {
243
fido_log_debug("%s: strdup", __func__);
244
goto fail;
245
}
246
247
fail:
248
free(ifdetail);
249
250
return (path);
251
}
252
253
#ifndef FIDO_HID_ANY
254
static bool
255
hid_ok(HDEVINFO devinfo, DWORD idx)
256
{
257
SP_DEVINFO_DATA devinfo_data;
258
wchar_t *parent = NULL;
259
DWORD parent_type = DEVPROP_TYPE_STRING;
260
DWORD len = 0;
261
bool ok = false;
262
263
memset(&devinfo_data, 0, sizeof(devinfo_data));
264
devinfo_data.cbSize = sizeof(devinfo_data);
265
266
if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) {
267
fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__);
268
goto fail;
269
}
270
271
if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
272
&DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false ||
273
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
274
fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__);
275
goto fail;
276
}
277
278
if ((parent = malloc(len)) == NULL) {
279
fido_log_debug("%s: malloc", __func__);
280
goto fail;
281
}
282
283
if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
284
&DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL,
285
0) == false) {
286
fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__);
287
goto fail;
288
}
289
290
ok = wcsncmp(parent, L"USB\\", 4) == 0;
291
fail:
292
free(parent);
293
294
return (ok);
295
}
296
#endif
297
298
static int
299
copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx,
300
SP_DEVICE_INTERFACE_DATA *ifdata)
301
{
302
HANDLE dev = INVALID_HANDLE_VALUE;
303
int ok = -1;
304
305
memset(di, 0, sizeof(*di));
306
307
if ((di->path = get_path(devinfo, ifdata)) == NULL) {
308
fido_log_debug("%s: get_path", __func__);
309
goto fail;
310
}
311
312
fido_log_debug("%s: path=%s", __func__, di->path);
313
314
#ifndef FIDO_HID_ANY
315
if (hid_ok(devinfo, idx) == false) {
316
fido_log_debug("%s: hid_ok", __func__);
317
goto fail;
318
}
319
#endif
320
321
dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
322
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
323
if (dev == INVALID_HANDLE_VALUE) {
324
fido_log_debug("%s: CreateFileA", __func__);
325
goto fail;
326
}
327
328
if (is_fido(dev) == false) {
329
fido_log_debug("%s: is_fido", __func__);
330
goto fail;
331
}
332
333
if (get_id(dev, &di->vendor_id, &di->product_id) < 0) {
334
fido_log_debug("%s: get_id", __func__);
335
goto fail;
336
}
337
338
if (get_manufacturer(dev, &di->manufacturer) < 0) {
339
fido_log_debug("%s: get_manufacturer", __func__);
340
di->manufacturer = strdup("");
341
}
342
343
if (get_product(dev, &di->product) < 0) {
344
fido_log_debug("%s: get_product", __func__);
345
di->product = strdup("");
346
}
347
348
if (di->manufacturer == NULL || di->product == NULL) {
349
fido_log_debug("%s: manufacturer/product", __func__);
350
goto fail;
351
}
352
353
ok = 0;
354
fail:
355
if (dev != INVALID_HANDLE_VALUE)
356
CloseHandle(dev);
357
358
if (ok < 0) {
359
free(di->path);
360
free(di->manufacturer);
361
free(di->product);
362
explicit_bzero(di, sizeof(*di));
363
}
364
365
return (ok);
366
}
367
368
int
369
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
370
{
371
GUID hid_guid = GUID_DEVINTERFACE_HID;
372
HDEVINFO devinfo = INVALID_HANDLE_VALUE;
373
SP_DEVICE_INTERFACE_DATA ifdata;
374
DWORD idx;
375
int r = FIDO_ERR_INTERNAL;
376
377
*olen = 0;
378
379
if (ilen == 0)
380
return (FIDO_OK); /* nothing to do */
381
if (devlist == NULL)
382
return (FIDO_ERR_INVALID_ARGUMENT);
383
384
if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
385
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) {
386
fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
387
goto fail;
388
}
389
390
ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
391
392
for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid,
393
idx, &ifdata) == true; idx++) {
394
if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) {
395
devlist[*olen].io = (fido_dev_io_t) {
396
fido_hid_open,
397
fido_hid_close,
398
fido_hid_read,
399
fido_hid_write,
400
};
401
if (++(*olen) == ilen)
402
break;
403
}
404
}
405
406
r = FIDO_OK;
407
fail:
408
if (devinfo != INVALID_HANDLE_VALUE)
409
SetupDiDestroyDeviceInfoList(devinfo);
410
411
return (r);
412
}
413
414
void *
415
fido_hid_open(const char *path)
416
{
417
struct hid_win *ctx;
418
419
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
420
return (NULL);
421
422
ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
423
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
424
FILE_FLAG_OVERLAPPED, NULL);
425
426
if (ctx->dev == INVALID_HANDLE_VALUE) {
427
free(ctx);
428
return (NULL);
429
}
430
431
if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE,
432
NULL)) == NULL) {
433
fido_log_debug("%s: CreateEventA", __func__);
434
fido_hid_close(ctx);
435
return (NULL);
436
}
437
438
if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 ||
439
get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) {
440
fido_log_debug("%s: get_report_len", __func__);
441
fido_hid_close(ctx);
442
return (NULL);
443
}
444
445
return (ctx);
446
}
447
448
void
449
fido_hid_close(void *handle)
450
{
451
struct hid_win *ctx = handle;
452
453
if (ctx->overlap.hEvent != NULL) {
454
if (ctx->report_pending) {
455
fido_log_debug("%s: report_pending", __func__);
456
if (CancelIoEx(ctx->dev, &ctx->overlap) == 0)
457
fido_log_debug("%s CancelIoEx: 0x%lx",
458
__func__, (u_long)GetLastError());
459
}
460
CloseHandle(ctx->overlap.hEvent);
461
}
462
463
explicit_bzero(ctx->report, sizeof(ctx->report));
464
CloseHandle(ctx->dev);
465
free(ctx);
466
}
467
468
int
469
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
470
{
471
(void)handle;
472
(void)sigmask;
473
474
return (FIDO_ERR_INTERNAL);
475
}
476
477
int
478
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
479
{
480
struct hid_win *ctx = handle;
481
DWORD n;
482
483
if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) {
484
fido_log_debug("%s: len %zu", __func__, len);
485
return (-1);
486
}
487
488
if (ctx->report_pending == 0) {
489
memset(&ctx->report, 0, sizeof(ctx->report));
490
ResetEvent(ctx->overlap.hEvent);
491
if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n,
492
&ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) {
493
CancelIo(ctx->dev);
494
fido_log_debug("%s: ReadFile", __func__);
495
return (-1);
496
}
497
ctx->report_pending = 1;
498
}
499
500
if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent,
501
(DWORD)ms) != WAIT_OBJECT_0)
502
return (0);
503
504
ctx->report_pending = 0;
505
506
if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) {
507
fido_log_debug("%s: GetOverlappedResult", __func__);
508
return (-1);
509
}
510
511
if (n != len + 1) {
512
fido_log_debug("%s: expected %zu, got %zu", __func__,
513
len + 1, (size_t)n);
514
return (-1);
515
}
516
517
memcpy(buf, ctx->report + 1, len);
518
explicit_bzero(ctx->report, sizeof(ctx->report));
519
520
return ((int)len);
521
}
522
523
int
524
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
525
{
526
struct hid_win *ctx = handle;
527
OVERLAPPED overlap;
528
DWORD n;
529
530
memset(&overlap, 0, sizeof(overlap));
531
532
if (len != ctx->report_out_len) {
533
fido_log_debug("%s: len %zu", __func__, len);
534
return (-1);
535
}
536
537
if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 &&
538
GetLastError() != ERROR_IO_PENDING) {
539
fido_log_debug("%s: WriteFile", __func__);
540
return (-1);
541
}
542
543
if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) {
544
fido_log_debug("%s: GetOverlappedResult", __func__);
545
return (-1);
546
}
547
548
if (n != len) {
549
fido_log_debug("%s: expected %zu, got %zu", __func__, len,
550
(size_t)n);
551
return (-1);
552
}
553
554
return ((int)len);
555
}
556
557
size_t
558
fido_hid_report_in_len(void *handle)
559
{
560
struct hid_win *ctx = handle;
561
562
return (ctx->report_in_len - 1);
563
}
564
565
size_t
566
fido_hid_report_out_len(void *handle)
567
{
568
struct hid_win *ctx = handle;
569
570
return (ctx->report_out_len - 1);
571
}
572
573