Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/libfido2/src/hid_freebsd.c
39478 views
1
/*
2
* Copyright (c) 2020-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/param.h>
9
10
#include <dev/usb/usb_ioctl.h>
11
#include <dev/usb/usbhid.h>
12
#if __FreeBSD_version >= 1300500
13
#include <dev/hid/hidraw.h>
14
#define USE_HIDRAW /* see usbhid(4) and hidraw(4) on FreeBSD 13+ */
15
#endif
16
17
#include <errno.h>
18
#include <unistd.h>
19
20
#include "fido.h"
21
22
#if defined(__MidnightBSD__)
23
#define UHID_VENDOR "MidnightBSD"
24
#else
25
#define UHID_VENDOR "FreeBSD"
26
#endif
27
28
#define MAX_UHID 64
29
30
struct hid_freebsd {
31
int fd;
32
size_t report_in_len;
33
size_t report_out_len;
34
sigset_t sigmask;
35
const sigset_t *sigmaskp;
36
};
37
38
static bool
39
is_fido(int fd)
40
{
41
char buf[64];
42
struct usb_gen_descriptor ugd;
43
uint32_t usage_page = 0;
44
45
memset(&buf, 0, sizeof(buf));
46
memset(&ugd, 0, sizeof(ugd));
47
48
ugd.ugd_report_type = UHID_FEATURE_REPORT;
49
ugd.ugd_data = buf;
50
ugd.ugd_maxlen = sizeof(buf);
51
52
if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) {
53
fido_log_error(errno, "%s: ioctl", __func__);
54
return (false);
55
}
56
if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data,
57
ugd.ugd_actlen, &usage_page) < 0) {
58
fido_log_debug("%s: fido_hid_get_usage", __func__);
59
return (false);
60
}
61
62
return (usage_page == 0xf1d0);
63
}
64
65
#ifdef USE_HIDRAW
66
static int
67
copy_info_hidraw(fido_dev_info_t *di, const char *path)
68
{
69
int fd = -1;
70
int ok = -1;
71
struct usb_device_info udi;
72
struct hidraw_devinfo devinfo;
73
char rawname[129];
74
75
memset(di, 0, sizeof(*di));
76
memset(&udi, 0, sizeof(udi));
77
memset(&devinfo, 0, sizeof(devinfo));
78
memset(rawname, 0, sizeof(rawname));
79
80
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
81
goto fail;
82
83
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
84
if (ioctl(fd, IOCTL_REQ(HIDIOCGRAWINFO), &devinfo) == -1 ||
85
ioctl(fd, IOCTL_REQ(HIDIOCGRAWNAME(128)), rawname) == -1 ||
86
(di->path = strdup(path)) == NULL ||
87
(di->manufacturer = strdup(UHID_VENDOR)) == NULL ||
88
(di->product = strdup(rawname)) == NULL)
89
goto fail;
90
di->vendor_id = devinfo.vendor;
91
di->product_id = devinfo.product;
92
} else {
93
if ((di->path = strdup(path)) == NULL ||
94
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
95
(di->product = strdup(udi.udi_product)) == NULL)
96
goto fail;
97
di->vendor_id = (int16_t)udi.udi_vendorNo;
98
di->product_id = (int16_t)udi.udi_productNo;
99
}
100
101
ok = 0;
102
fail:
103
if (fd != -1 && close(fd) == -1)
104
fido_log_error(errno, "%s: close %s", __func__, path);
105
106
if (ok < 0) {
107
free(di->path);
108
free(di->manufacturer);
109
free(di->product);
110
explicit_bzero(di, sizeof(*di));
111
}
112
113
return (ok);
114
}
115
#endif /* USE_HIDRAW */
116
117
static int
118
copy_info_uhid(fido_dev_info_t *di, const char *path)
119
{
120
int fd = -1;
121
int ok = -1;
122
struct usb_device_info udi;
123
124
memset(di, 0, sizeof(*di));
125
memset(&udi, 0, sizeof(udi));
126
127
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
128
goto fail;
129
130
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
131
fido_log_error(errno, "%s: ioctl", __func__);
132
strlcpy(udi.udi_vendor, UHID_VENDOR, sizeof(udi.udi_vendor));
133
strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product));
134
udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */
135
}
136
137
if ((di->path = strdup(path)) == NULL ||
138
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
139
(di->product = strdup(udi.udi_product)) == NULL)
140
goto fail;
141
di->vendor_id = (int16_t)udi.udi_vendorNo;
142
di->product_id = (int16_t)udi.udi_productNo;
143
144
ok = 0;
145
fail:
146
if (fd != -1 && close(fd) == -1)
147
fido_log_error(errno, "%s: close %s", __func__, path);
148
149
if (ok < 0) {
150
free(di->path);
151
free(di->manufacturer);
152
free(di->product);
153
explicit_bzero(di, sizeof(*di));
154
}
155
156
return (ok);
157
}
158
159
int
160
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
161
{
162
char path[64];
163
size_t i;
164
165
if (ilen == 0)
166
return (FIDO_OK); /* nothing to do */
167
168
if (devlist == NULL || olen == NULL)
169
return (FIDO_ERR_INVALID_ARGUMENT);
170
171
*olen = 0;
172
173
#ifdef USE_HIDRAW
174
for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
175
snprintf(path, sizeof(path), "/dev/hidraw%zu", i);
176
if (copy_info_hidraw(&devlist[*olen], path) == 0) {
177
devlist[*olen].io = (fido_dev_io_t) {
178
fido_hid_open,
179
fido_hid_close,
180
fido_hid_read,
181
fido_hid_write,
182
};
183
++(*olen);
184
}
185
}
186
/* hidraw(4) is preferred over uhid(4) */
187
if (*olen != 0)
188
return (FIDO_OK);
189
#endif /* USE_HIDRAW */
190
191
for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
192
snprintf(path, sizeof(path), "/dev/uhid%zu", i);
193
if (copy_info_uhid(&devlist[*olen], path) == 0) {
194
devlist[*olen].io = (fido_dev_io_t) {
195
fido_hid_open,
196
fido_hid_close,
197
fido_hid_read,
198
fido_hid_write,
199
};
200
++(*olen);
201
}
202
}
203
204
return (FIDO_OK);
205
}
206
207
void *
208
fido_hid_open(const char *path)
209
{
210
char buf[64];
211
struct hid_freebsd *ctx;
212
struct usb_gen_descriptor ugd;
213
int r;
214
215
memset(&buf, 0, sizeof(buf));
216
memset(&ugd, 0, sizeof(ugd));
217
218
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
219
return (NULL);
220
221
if ((ctx->fd = fido_hid_unix_open(path)) == -1) {
222
free(ctx);
223
return (NULL);
224
}
225
226
ugd.ugd_report_type = UHID_FEATURE_REPORT;
227
ugd.ugd_data = buf;
228
ugd.ugd_maxlen = sizeof(buf);
229
230
/*
231
* N.B. if ctx->fd is an hidraw(4) device, the ioctl() below puts it in
232
* uhid(4) compat mode, which we need to keep fido_hid_write() as-is.
233
*/
234
if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) ||
235
ugd.ugd_actlen > sizeof(buf) ||
236
fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen,
237
&ctx->report_in_len, &ctx->report_out_len) < 0) {
238
if (r == -1)
239
fido_log_error(errno, "%s: ioctl", __func__);
240
fido_log_debug("%s: using default report sizes", __func__);
241
ctx->report_in_len = CTAP_MAX_REPORT_LEN;
242
ctx->report_out_len = CTAP_MAX_REPORT_LEN;
243
}
244
245
return (ctx);
246
}
247
248
void
249
fido_hid_close(void *handle)
250
{
251
struct hid_freebsd *ctx = handle;
252
253
if (close(ctx->fd) == -1)
254
fido_log_error(errno, "%s: close", __func__);
255
256
free(ctx);
257
}
258
259
int
260
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
261
{
262
struct hid_freebsd *ctx = handle;
263
264
ctx->sigmask = *sigmask;
265
ctx->sigmaskp = &ctx->sigmask;
266
267
return (FIDO_OK);
268
}
269
270
int
271
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
272
{
273
struct hid_freebsd *ctx = handle;
274
ssize_t r;
275
276
if (len != ctx->report_in_len) {
277
fido_log_debug("%s: len %zu", __func__, len);
278
return (-1);
279
}
280
281
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
282
fido_log_debug("%s: fd not ready", __func__);
283
return (-1);
284
}
285
286
if ((r = read(ctx->fd, buf, len)) == -1) {
287
fido_log_error(errno, "%s: read", __func__);
288
return (-1);
289
}
290
291
if (r < 0 || (size_t)r != len) {
292
fido_log_debug("%s: %zd != %zu", __func__, r, len);
293
return (-1);
294
}
295
296
return ((int)r);
297
}
298
299
int
300
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
301
{
302
struct hid_freebsd *ctx = handle;
303
ssize_t r;
304
305
if (len != ctx->report_out_len + 1) {
306
fido_log_debug("%s: len %zu", __func__, len);
307
return (-1);
308
}
309
310
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
311
fido_log_error(errno, "%s: write", __func__);
312
return (-1);
313
}
314
315
if (r < 0 || (size_t)r != len - 1) {
316
fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);
317
return (-1);
318
}
319
320
return ((int)len);
321
}
322
323
size_t
324
fido_hid_report_in_len(void *handle)
325
{
326
struct hid_freebsd *ctx = handle;
327
328
return (ctx->report_in_len);
329
}
330
331
size_t
332
fido_hid_report_out_len(void *handle)
333
{
334
struct hid_freebsd *ctx = handle;
335
336
return (ctx->report_out_len);
337
}
338
339