]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libfido2/src/hid_hidapi.c
libfido2: update to 1.13.0
[FreeBSD/FreeBSD.git] / contrib / libfido2 / src / hid_hidapi.c
1 /*
2  * Copyright (c) 2019 Google LLC. 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 #ifdef __linux__
9 #include <sys/ioctl.h>
10 #include <linux/hidraw.h>
11 #include <linux/input.h>
12 #include <fcntl.h>
13 #endif
14
15 #include <errno.h>
16 #include <hidapi.h>
17 #include <wchar.h>
18
19 #include "fido.h"
20
21 struct hid_hidapi {
22         void *handle;
23         size_t report_in_len;
24         size_t report_out_len;
25 };
26
27 static size_t
28 fido_wcslen(const wchar_t *wcs)
29 {
30         size_t l = 0;
31         while (*wcs++ != L'\0')
32                 l++;
33         return l;
34 }
35
36 static char *
37 wcs_to_cs(const wchar_t *wcs)
38 {
39         char *cs;
40         size_t i;
41
42         if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL)
43                 return NULL;
44
45         for (i = 0; i < fido_wcslen(wcs); i++) {
46                 if (wcs[i] >= 128) {
47                         /* give up on parsing non-ASCII text */
48                         free(cs);
49                         return strdup("hidapi device");
50                 }
51                 cs[i] = (char)wcs[i];
52         }
53
54         return cs;
55 }
56
57 static int
58 copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
59 {
60         memset(di, 0, sizeof(*di));
61
62         if (d->path != NULL)
63                 di->path = strdup(d->path);
64         else
65                 di->path = strdup("");
66
67         if (d->manufacturer_string != NULL)
68                 di->manufacturer = wcs_to_cs(d->manufacturer_string);
69         else
70                 di->manufacturer = strdup("");
71
72         if (d->product_string != NULL)
73                 di->product = wcs_to_cs(d->product_string);
74         else
75                 di->product = strdup("");
76
77         if (di->path == NULL ||
78             di->manufacturer == NULL ||
79             di->product == NULL) {
80                 free(di->path);
81                 free(di->manufacturer);
82                 free(di->product);
83                 explicit_bzero(di, sizeof(*di));
84                 return -1;
85         }
86
87         di->product_id = (int16_t)d->product_id;
88         di->vendor_id = (int16_t)d->vendor_id;
89         di->io = (fido_dev_io_t) {
90                 &fido_hid_open,
91                 &fido_hid_close,
92                 &fido_hid_read,
93                 &fido_hid_write,
94         };
95
96         return 0;
97 }
98
99 #ifdef __linux__
100 static int
101 get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
102 {
103         int fd;
104         int s = -1;
105         int ok = -1;
106
107         if ((fd = fido_hid_unix_open(path)) == -1) {
108                 fido_log_debug("%s: fido_hid_unix_open", __func__);
109                 return -1;
110         }
111
112         if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 ||
113             (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
114                 fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
115                 goto fail;
116         }
117
118         hrd->size = (unsigned)s;
119
120         if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) {
121                 fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
122                 goto fail;
123         }
124
125         ok = 0;
126 fail:
127         if (fd != -1)
128                 close(fd);
129
130         return ok;
131 }
132
133 static bool
134 is_fido(const struct hid_device_info *hdi)
135 {
136         uint32_t usage_page = 0;
137         struct hidraw_report_descriptor *hrd;
138
139         if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
140             get_report_descriptor(hdi->path, hrd) < 0 ||
141             fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
142                 usage_page = 0;
143
144         free(hrd);
145
146         return usage_page == 0xf1d0;
147 }
148 #elif defined(_WIN32) || defined(__APPLE__)
149 static bool
150 is_fido(const struct hid_device_info *hdi)
151 {
152         return hdi->usage_page == 0xf1d0;
153 }
154 #else
155 static bool
156 is_fido(const struct hid_device_info *hdi)
157 {
158         (void)hdi;
159         fido_log_debug("%s: assuming FIDO HID", __func__);
160         return true;
161 }
162 #endif
163
164 void *
165 fido_hid_open(const char *path)
166 {
167         struct hid_hidapi *ctx;
168
169         if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
170                 return (NULL);
171         }
172
173         if ((ctx->handle = hid_open_path(path)) == NULL) {
174                 free(ctx);
175                 return (NULL);
176         }
177
178         ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN;
179
180         return ctx;
181 }
182
183 void
184 fido_hid_close(void *handle)
185 {
186         struct hid_hidapi *ctx = handle;
187
188         hid_close(ctx->handle);
189         free(ctx);
190 }
191
192 int
193 fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
194 {
195         (void)handle;
196         (void)sigmask;
197
198         return (FIDO_ERR_INTERNAL);
199 }
200
201 int
202 fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
203 {
204         struct hid_hidapi *ctx = handle;
205
206         if (len != ctx->report_in_len) {
207                 fido_log_debug("%s: len %zu", __func__, len);
208                 return -1;
209         }
210
211         return hid_read_timeout(ctx->handle, buf, len, ms);
212 }
213
214 int
215 fido_hid_write(void *handle, const unsigned char *buf, size_t len)
216 {
217         struct hid_hidapi *ctx = handle;
218
219         if (len != ctx->report_out_len + 1) {
220                 fido_log_debug("%s: len %zu", __func__, len);
221                 return -1;
222         }
223
224         return hid_write(ctx->handle, buf, len);
225 }
226
227 int
228 fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
229 {
230         struct hid_device_info *hdi;
231
232         *olen = 0;
233
234         if (ilen == 0)
235                 return FIDO_OK; /* nothing to do */
236         if (devlist == NULL)
237                 return FIDO_ERR_INVALID_ARGUMENT;
238         if ((hdi = hid_enumerate(0, 0)) == NULL)
239                 return FIDO_OK; /* nothing to do */
240
241         for (struct hid_device_info *d = hdi; d != NULL; d = d->next) {
242                 if (is_fido(d) == false)
243                         continue;
244                 if (copy_info(&devlist[*olen], d) == 0) {
245                         if (++(*olen) == ilen)
246                                 break;
247                 }
248         }
249
250         hid_free_enumeration(hdi);
251
252         return FIDO_OK;
253 }
254
255 size_t
256 fido_hid_report_in_len(void *handle)
257 {
258         struct hid_hidapi *ctx = handle;
259
260         return (ctx->report_in_len);
261 }
262
263 size_t
264 fido_hid_report_out_len(void *handle)
265 {
266         struct hid_hidapi *ctx = handle;
267
268         return (ctx->report_out_len);
269 }