xref: /freebsd/contrib/libfido2/tools/token.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
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_algorithms(const fido_cbor_info_t *ci)
99 {
100 	const char *cose, *type;
101 	size_t len;
102 
103 	if ((len = fido_cbor_info_algorithm_count(ci)) == 0)
104 		return;
105 
106 	printf("algorithms: ");
107 
108 	for (size_t i = 0; i < len; i++) {
109 		cose = type = "unknown";
110 		switch (fido_cbor_info_algorithm_cose(ci, i)) {
111 		case COSE_EDDSA:
112 			cose = "eddsa";
113 			break;
114 		case COSE_ES256:
115 			cose = "es256";
116 			break;
117 		case COSE_RS256:
118 			cose = "rs256";
119 			break;
120 		}
121 		if (fido_cbor_info_algorithm_type(ci, i) != NULL)
122 			type = fido_cbor_info_algorithm_type(ci, i);
123 		printf("%s%s (%s)", i > 0 ? ", " : "", cose, type);
124 	}
125 
126 	printf("\n");
127 }
128 
129 static void
130 print_aaguid(const unsigned char *buf, size_t buflen)
131 {
132 	printf("aaguid: ");
133 
134 	while (buflen--)
135 		printf("%02x", *buf++);
136 
137 	printf("\n");
138 }
139 
140 static void
141 print_maxmsgsiz(uint64_t maxmsgsiz)
142 {
143 	printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
144 }
145 
146 static void
147 print_maxcredcntlst(uint64_t maxcredcntlst)
148 {
149 	printf("maxcredcntlst: %d\n", (int)maxcredcntlst);
150 }
151 
152 static void
153 print_maxcredidlen(uint64_t maxcredidlen)
154 {
155 	printf("maxcredlen: %d\n", (int)maxcredidlen);
156 }
157 
158 static void
159 print_fwversion(uint64_t fwversion)
160 {
161 	printf("fwversion: 0x%x\n", (int)fwversion);
162 }
163 
164 static void
165 print_byte_array(const char *label, const uint8_t *ba, size_t len)
166 {
167 	if (len == 0)
168 		return;
169 
170 	printf("%s: ", label);
171 
172 	for (size_t i = 0; i < len; i++)
173 		printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
174 
175 	printf("\n");
176 }
177 
178 int
179 token_info(int argc, char **argv, char *path)
180 {
181 	char			*cred_id = NULL;
182 	char			*rp_id = NULL;
183 	fido_cbor_info_t	*ci = NULL;
184 	fido_dev_t		*dev = NULL;
185 	int			 ch;
186 	int			 credman = 0;
187 	int			 r;
188 	int			 retrycnt;
189 
190 	optind = 1;
191 
192 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
193 		switch (ch) {
194 		case 'c':
195 			credman = 1;
196 			break;
197 		case 'i':
198 			cred_id = optarg;
199 			break;
200 		case 'k':
201 			rp_id = optarg;
202 			break;
203 		default:
204 			break; /* ignore */
205 		}
206 	}
207 
208 	if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL)))
209 		usage();
210 
211 	dev = open_dev(path);
212 
213 	if (credman)
214 		return (credman_get_metadata(dev, path));
215 	if (cred_id && rp_id)
216 		return (credman_print_rk(dev, path, rp_id, cred_id));
217 	if (cred_id || rp_id)
218 		usage();
219 
220 	print_attr(dev);
221 
222 	if (fido_dev_is_fido2(dev) == false)
223 		goto end;
224 	if ((ci = fido_cbor_info_new()) == NULL)
225 		errx(1, "fido_cbor_info_new");
226 	if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
227 		errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
228 
229 	/* print supported protocol versions */
230 	print_str_array("version", fido_cbor_info_versions_ptr(ci),
231 	    fido_cbor_info_versions_len(ci));
232 
233 	/* print supported extensions */
234 	print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
235 	    fido_cbor_info_extensions_len(ci));
236 
237 	/* print supported transports */
238 	print_str_array("transport", fido_cbor_info_transports_ptr(ci),
239 	    fido_cbor_info_transports_len(ci));
240 
241 	/* print supported algorithms */
242 	print_algorithms(ci);
243 
244 	/* print aaguid */
245 	print_aaguid(fido_cbor_info_aaguid_ptr(ci),
246 	    fido_cbor_info_aaguid_len(ci));
247 
248 	/* print supported options */
249 	print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
250 	    fido_cbor_info_options_value_ptr(ci),
251 	    fido_cbor_info_options_len(ci));
252 
253 	/* print maximum message size */
254 	print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
255 
256 	/* print maximum number of credentials allowed in credential lists */
257 	print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci));
258 
259 	/* print maximum length of a credential ID */
260 	print_maxcredidlen(fido_cbor_info_maxcredidlen(ci));
261 
262 	/* print firmware version */
263 	print_fwversion(fido_cbor_info_fwversion(ci));
264 
265 	/* print supported pin protocols */
266 	print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
267 	    fido_cbor_info_protocols_len(ci));
268 
269 	if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK)
270 		printf("pin retries: undefined\n");
271 	else
272 		printf("pin retries: %d\n", retrycnt);
273 
274 	if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK)
275 		printf("uv retries: undefined\n");
276 	else
277 		printf("uv retries: %d\n", retrycnt);
278 
279 	bio_info(dev);
280 
281 	fido_cbor_info_free(&ci);
282 end:
283 	fido_dev_close(dev);
284 	fido_dev_free(&dev);
285 
286 	exit(0);
287 }
288 
289 int
290 token_reset(char *path)
291 {
292 	fido_dev_t *dev = NULL;
293 	int r;
294 
295 	if (path == NULL)
296 		usage();
297 
298 	dev = open_dev(path);
299 	if ((r = fido_dev_reset(dev)) != FIDO_OK)
300 		errx(1, "fido_dev_reset: %s", fido_strerr(r));
301 
302 	fido_dev_close(dev);
303 	fido_dev_free(&dev);
304 
305 	exit(0);
306 }
307 
308 int
309 token_get(int argc, char **argv, char *path)
310 {
311 	char	*id = NULL;
312 	char	*key = NULL;
313 	char	*name = NULL;
314 	int	 blob = 0;
315 	int	 ch;
316 
317 	optind = 1;
318 
319 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
320 		switch (ch) {
321 		case 'b':
322 			blob = 1;
323 			break;
324 		case 'i':
325 			id = optarg;
326 			break;
327 		case 'k':
328 			key = optarg;
329 			break;
330 		case 'n':
331 			name = optarg;
332 			break;
333 		default:
334 			break; /* ignore */
335 		}
336 	}
337 
338 	argc -= optind;
339 	argv += optind;
340 
341 	if (blob == 0 || argc != 2)
342 		usage();
343 
344 	return blob_get(path, key, name, id, argv[0]);
345 }
346 
347 int
348 token_set(int argc, char **argv, char *path)
349 {
350 	char	*id = NULL;
351 	char	*key = NULL;
352 	char	*len = NULL;
353 	char	*display_name = NULL;
354 	char	*name = NULL;
355 	int	 blob = 0;
356 	int	 cred = 0;
357 	int	 ch;
358 	int	 enroll = 0;
359 	int	 ea = 0;
360 	int	 uv = 0;
361 	bool	 force = false;
362 
363 	optind = 1;
364 
365 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
366 		switch (ch) {
367 		case 'a':
368 			ea = 1;
369 			break;
370 		case 'b':
371 			blob = 1;
372 			break;
373 		case 'c':
374 			cred = 1;
375 			break;
376 		case 'e':
377 			enroll = 1;
378 			break;
379 		case 'f':
380 			force = true;
381 			break;
382 		case 'i':
383 			id = optarg;
384 			break;
385 		case 'k':
386 			key = optarg;
387 			break;
388 		case 'l':
389 			len = optarg;
390 			break;
391 		case 'p':
392 			display_name = optarg;
393 			break;
394 		case 'n':
395 			name = optarg;
396 			break;
397 		case 'u':
398 			uv = 1;
399 			break;
400 		default:
401 			break; /* ignore */
402 		}
403 	}
404 
405 	argc -= optind;
406 	argv += optind;
407 
408 	if (path == NULL)
409 		usage();
410 
411 	if (blob) {
412 		if (argc != 2)
413 			usage();
414 		return (blob_set(path, key, name, id, argv[0]));
415 	}
416 
417 	if (cred) {
418 		if (!id || !key)
419 			usage();
420 		if (!name && !display_name)
421 			usage();
422 		return (credman_update_rk(path, key, id, name, display_name));
423 	}
424 
425 	if (enroll) {
426 		if (ea || uv)
427 			usage();
428 		if (id && name)
429 			return (bio_set_name(path, id, name));
430 		if (!id && !name)
431 			return (bio_enroll(path));
432 		usage();
433 	}
434 
435 	if (ea) {
436 		if (uv)
437 			usage();
438 		return (config_entattest(path));
439 	}
440 
441 	if (len)
442 		return (config_pin_minlen(path, len));
443 	if (force)
444 		return (config_force_pin_change(path));
445 	if (uv)
446 		return (config_always_uv(path, 1));
447 
448 	return (pin_set(path));
449 }
450 
451 int
452 token_list(int argc, char **argv, char *path)
453 {
454 	fido_dev_info_t *devlist;
455 	size_t ndevs;
456 	const char *rp_id = NULL;
457 	int blobs = 0;
458 	int enrolls = 0;
459 	int keys = 0;
460 	int rplist = 0;
461 	int ch;
462 	int r;
463 
464 	optind = 1;
465 
466 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
467 		switch (ch) {
468 		case 'b':
469 			blobs = 1;
470 			break;
471 		case 'e':
472 			enrolls = 1;
473 			break;
474 		case 'k':
475 			keys = 1;
476 			rp_id = optarg;
477 			break;
478 		case 'r':
479 			rplist = 1;
480 			break;
481 		default:
482 			break; /* ignore */
483 		}
484 	}
485 
486 	if (blobs || enrolls || keys || rplist) {
487 		if (path == NULL)
488 			usage();
489 		if (blobs)
490 			return (blob_list(path));
491 		if (enrolls)
492 			return (bio_list(path));
493 		if (keys)
494 			return (credman_list_rk(path, rp_id));
495 		if (rplist)
496 			return (credman_list_rp(path));
497 		/* NOTREACHED */
498 	}
499 
500 	if ((devlist = fido_dev_info_new(64)) == NULL)
501 		errx(1, "fido_dev_info_new");
502 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
503 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
504 
505 	for (size_t i = 0; i < ndevs; i++) {
506 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
507 		printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
508 		    fido_dev_info_path(di),
509 		    (uint16_t)fido_dev_info_vendor(di),
510 		    (uint16_t)fido_dev_info_product(di),
511 		    fido_dev_info_manufacturer_string(di),
512 		    fido_dev_info_product_string(di));
513 	}
514 
515 	fido_dev_info_free(&devlist, ndevs);
516 
517 	exit(0);
518 }
519 
520 int
521 token_delete(int argc, char **argv, char *path)
522 {
523 	char		*id = NULL;
524 	char		*key = NULL;
525 	char		*name = NULL;
526 	int		 blob = 0;
527 	int		 ch;
528 	int		 enroll = 0;
529 	int		 uv = 0;
530 
531 	optind = 1;
532 
533 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
534 		switch (ch) {
535 		case 'b':
536 			blob = 1;
537 			break;
538 		case 'e':
539 			enroll = 1;
540 			break;
541 		case 'i':
542 			id = optarg;
543 			break;
544 		case 'k':
545 			key = optarg;
546 			break;
547 		case 'n':
548 			name = optarg;
549 			break;
550 		case 'u':
551 			uv = 1;
552 			break;
553 		default:
554 			break; /* ignore */
555 		}
556 	}
557 
558 	if (path == NULL)
559 		usage();
560 
561 	if (blob)
562 		return (blob_delete(path, key, name, id));
563 
564 	if (id) {
565 		if (uv)
566 			usage();
567 		if (enroll == 0)
568 			return (credman_delete_rk(path, id));
569 		return (bio_delete(path, id));
570 	}
571 
572 	if (uv == 0)
573 		usage();
574 
575 	return (config_always_uv(path, 0));
576 }
577