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