xref: /netbsd-src/external/bsd/libfido2/dist/tools/token.c (revision d90047b5d07facf36e6c01dcc0bded8997ce9cc2)
1 /*
2  * Copyright (c) 2018 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 <fido.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
15 
16 #include "../openbsd-compat/openbsd-compat.h"
17 #include "extern.h"
18 
19 static void
20 format_flags(char *ret, size_t retlen, uint8_t flags)
21 {
22 	memset(ret, 0, retlen);
23 
24 	if (flags & FIDO_CAP_WINK) {
25 		if (strlcat(ret, "wink,", retlen) >= retlen)
26 			goto toolong;
27 	} else {
28 		if (strlcat(ret, "nowink,", retlen) >= retlen)
29 			goto toolong;
30 	}
31 
32 	if (flags & FIDO_CAP_CBOR) {
33 		if (strlcat(ret, " cbor,", retlen) >= retlen)
34 			goto toolong;
35 	} else {
36 		if (strlcat(ret, " nocbor,", retlen) >= retlen)
37 			goto toolong;
38 	}
39 
40 	if (flags & FIDO_CAP_NMSG) {
41 		if (strlcat(ret, " nomsg", retlen) >= retlen)
42 			goto toolong;
43 	} else {
44 		if (strlcat(ret, " msg", retlen) >= retlen)
45 			goto toolong;
46 	}
47 
48 	return;
49 toolong:
50 	strlcpy(ret, "toolong", retlen);
51 }
52 
53 static void
54 print_attr(const fido_dev_t *dev)
55 {
56 	char flags_txt[128];
57 
58 	printf("proto: 0x%02x\n", fido_dev_protocol(dev));
59 	printf("major: 0x%02x\n", fido_dev_major(dev));
60 	printf("minor: 0x%02x\n", fido_dev_minor(dev));
61 	printf("build: 0x%02x\n", fido_dev_build(dev));
62 
63 	format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev));
64 	printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt);
65 }
66 
67 static void
68 print_str_array(const char *label, char * const *sa, size_t len)
69 {
70 	if (len == 0)
71 		return;
72 
73 	printf("%s strings: ", label);
74 
75 	for (size_t i = 0; i < len; i++)
76 		printf("%s%s", i > 0 ? ", " : "", sa[i]);
77 
78 	printf("\n");
79 }
80 
81 static void
82 print_opt_array(const char *label, char * const *name, const bool *value,
83     size_t len)
84 {
85 	if (len == 0)
86 		return;
87 
88 	printf("%s: ", label);
89 
90 	for (size_t i = 0; i < len; i++)
91 		printf("%s%s%s", i > 0 ? ", " : "",
92 		    value[i] ? "" : "no", name[i]);
93 
94 	printf("\n");
95 }
96 
97 static void
98 print_aaguid(const unsigned char *buf, size_t buflen)
99 {
100 	printf("aaguid: ");
101 
102 	while (buflen--)
103 		printf("%02x", *buf++);
104 
105 	printf("\n");
106 }
107 
108 static void
109 print_maxmsgsiz(uint64_t maxmsgsiz)
110 {
111 	printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
112 }
113 
114 static void
115 print_byte_array(const char *label, const uint8_t *ba, size_t len)
116 {
117 	if (len == 0)
118 		return;
119 
120 	printf("%s: ", label);
121 
122 	for (size_t i = 0; i < len; i++)
123 		printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
124 
125 	printf("\n");
126 }
127 
128 int
129 token_info(int argc, char **argv, char *path)
130 {
131 	char			*cred_id = NULL;
132 	char			*rp_id = NULL;
133 	fido_cbor_info_t	*ci = NULL;
134 	fido_dev_t		*dev = NULL;
135 	int			 ch;
136 	int			 credman = 0;
137 	int			 r;
138 	int			 retrycnt;
139 
140 	optind = 1;
141 
142 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
143 		switch (ch) {
144 		case 'c':
145 			credman = 1;
146 			break;
147 		case 'i':
148 			cred_id = optarg;
149 			break;
150 		case 'k':
151 			rp_id = optarg;
152 			break;
153 		default:
154 			break; /* ignore */
155 		}
156 	}
157 
158 	if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL)))
159 		usage();
160 
161 	dev = open_dev(path);
162 
163 	if (credman)
164 		return (credman_get_metadata(dev, path));
165 	if (cred_id && rp_id)
166 		return (credman_print_rk(dev, path, rp_id, cred_id));
167 	if (cred_id || rp_id)
168 		usage();
169 
170 	print_attr(dev);
171 
172 	if (fido_dev_is_fido2(dev) == false)
173 		goto end;
174 	if ((ci = fido_cbor_info_new()) == NULL)
175 		errx(1, "fido_cbor_info_new");
176 	if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
177 		errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
178 
179 	/* print supported protocol versions */
180 	print_str_array("version", fido_cbor_info_versions_ptr(ci),
181 	    fido_cbor_info_versions_len(ci));
182 
183 	/* print supported extensions */
184 	print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
185 	    fido_cbor_info_extensions_len(ci));
186 
187 	/* print aaguid */
188 	print_aaguid(fido_cbor_info_aaguid_ptr(ci),
189 	    fido_cbor_info_aaguid_len(ci));
190 
191 	/* print supported options */
192 	print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
193 	    fido_cbor_info_options_value_ptr(ci),
194 	    fido_cbor_info_options_len(ci));
195 
196 	/* print maximum message size */
197 	print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
198 
199 	/* print supported pin protocols */
200 	print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
201 	    fido_cbor_info_protocols_len(ci));
202 
203 	if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK)
204 		printf("pin retries: undefined\n");
205 	else
206 		printf("pin retries: %d\n", retrycnt);
207 
208 	bio_info(dev);
209 
210 	fido_cbor_info_free(&ci);
211 end:
212 	fido_dev_close(dev);
213 	fido_dev_free(&dev);
214 
215 	exit(0);
216 }
217 
218 int
219 token_reset(char *path)
220 {
221 	fido_dev_t *dev = NULL;
222 	int r;
223 
224 	if (path == NULL)
225 		usage();
226 
227 	dev = open_dev(path);
228 	if ((r = fido_dev_reset(dev)) != FIDO_OK)
229 		errx(1, "fido_dev_reset: %s", fido_strerr(r));
230 
231 	fido_dev_close(dev);
232 	fido_dev_free(&dev);
233 
234 	exit(0);
235 }
236 
237 int
238 token_set(int argc, char **argv, char *path)
239 {
240 	char	*id = NULL;
241 	char	*name = NULL;
242 	int	 ch;
243 	int	 enroll = 0;
244 
245 	optind = 1;
246 
247 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
248 		switch (ch) {
249 		case 'e':
250 			enroll = 1;
251 			break;
252 		case 'i':
253 			id = optarg;
254 			break;
255 		case 'n':
256 			name = optarg;
257 			break;
258 		default:
259 			break; /* ignore */
260 		}
261 	}
262 
263 	if (enroll) {
264 		if (id && name)
265 			return (bio_set_name(path, id, name));
266 		if (!id && !name)
267 			return (bio_enroll(path));
268 		usage();
269 	}
270 
271 	return (pin_set(path));
272 }
273 
274 int
275 token_list(int argc, char **argv, char *path)
276 {
277 	fido_dev_info_t *devlist;
278 	size_t ndevs;
279 	const char *rp_id = NULL;
280 	int enrolls = 0;
281 	int keys = 0;
282 	int rplist = 0;
283 	int ch;
284 	int r;
285 
286 	optind = 1;
287 
288 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
289 		switch (ch) {
290 		case 'e':
291 			enrolls = 1;
292 			break;
293 		case 'k':
294 			keys = 1;
295 			rp_id = optarg;
296 			break;
297 		case 'r':
298 			rplist = 1;
299 			break;
300 		default:
301 			break; /* ignore */
302 		}
303 	}
304 
305 	if (enrolls)
306 		return (bio_list(path));
307 	if (keys)
308 		return (credman_list_rk(path, rp_id));
309 	if (rplist)
310 		return (credman_list_rp(path));
311 
312 	if ((devlist = fido_dev_info_new(64)) == NULL)
313 		errx(1, "fido_dev_info_new");
314 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
315 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
316 
317 	for (size_t i = 0; i < ndevs; i++) {
318 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
319 		printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
320 		    fido_dev_info_path(di),
321 		    (uint16_t)fido_dev_info_vendor(di),
322 		    (uint16_t)fido_dev_info_product(di),
323 		    fido_dev_info_manufacturer_string(di),
324 		    fido_dev_info_product_string(di));
325 	}
326 
327 	fido_dev_info_free(&devlist, ndevs);
328 
329 	exit(0);
330 }
331 
332 int
333 token_delete(int argc, char **argv, char *path)
334 {
335 	char		*id = NULL;
336 	fido_dev_t	*dev = NULL;
337 	int		 ch;
338 	int		 enroll = 0;
339 
340 	optind = 1;
341 
342 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
343 		switch (ch) {
344 		case 'e':
345 			enroll = 1;
346 			break;
347 		case 'i':
348 			id = optarg;
349 			break;
350 		default:
351 			break; /* ignore */
352 		}
353 	}
354 
355 	if (path == NULL || id == NULL)
356 		usage();
357 
358 	dev = open_dev(path);
359 
360 	if (id && !enroll)
361 		return (credman_delete_rk(dev, path, id));
362 
363 	return (bio_delete(dev, path, id));
364 }
365