xref: /freebsd/contrib/libfido2/tools/token.c (revision bdd1243d)
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 	char	*rpid = NULL;
356 	int	 blob = 0;
357 	int	 cred = 0;
358 	int	 ch;
359 	int	 enroll = 0;
360 	int	 ea = 0;
361 	int	 uv = 0;
362 	bool	 force = false;
363 
364 	optind = 1;
365 
366 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
367 		switch (ch) {
368 		case 'a':
369 			ea = 1;
370 			break;
371 		case 'b':
372 			blob = 1;
373 			break;
374 		case 'c':
375 			cred = 1;
376 			break;
377 		case 'e':
378 			enroll = 1;
379 			break;
380 		case 'f':
381 			force = true;
382 			break;
383 		case 'i':
384 			id = optarg;
385 			break;
386 		case 'k':
387 			key = optarg;
388 			break;
389 		case 'l':
390 			len = optarg;
391 			break;
392 		case 'p':
393 			display_name = optarg;
394 			break;
395 		case 'm':
396 			rpid = optarg;
397 			break;
398 		case 'n':
399 			name = optarg;
400 			break;
401 		case 'u':
402 			uv = 1;
403 			break;
404 		default:
405 			break; /* ignore */
406 		}
407 	}
408 
409 	argc -= optind;
410 	argv += optind;
411 
412 	if (path == NULL)
413 		usage();
414 
415 	if (blob) {
416 		if (argc != 2)
417 			usage();
418 		return (blob_set(path, key, name, id, argv[0]));
419 	}
420 
421 	if (cred) {
422 		if (!id || !key)
423 			usage();
424 		if (!name && !display_name)
425 			usage();
426 		return (credman_update_rk(path, key, id, name, display_name));
427 	}
428 
429 	if (enroll) {
430 		if (ea || uv)
431 			usage();
432 		if (id && name)
433 			return (bio_set_name(path, id, name));
434 		if (!id && !name)
435 			return (bio_enroll(path));
436 		usage();
437 	}
438 
439 	if (ea) {
440 		if (uv)
441 			usage();
442 		return (config_entattest(path));
443 	}
444 
445 	if (len)
446 		return (config_pin_minlen(path, len));
447 	if (rpid)
448 		return (config_pin_minlen_rpid(path, rpid));
449 	if (force)
450 		return (config_force_pin_change(path));
451 	if (uv)
452 		return (config_always_uv(path, 1));
453 
454 	return (pin_set(path));
455 }
456 
457 int
458 token_list(int argc, char **argv, char *path)
459 {
460 	fido_dev_info_t *devlist;
461 	size_t ndevs;
462 	const char *rp_id = NULL;
463 	int blobs = 0;
464 	int enrolls = 0;
465 	int keys = 0;
466 	int rplist = 0;
467 	int ch;
468 	int r;
469 
470 	optind = 1;
471 
472 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
473 		switch (ch) {
474 		case 'b':
475 			blobs = 1;
476 			break;
477 		case 'e':
478 			enrolls = 1;
479 			break;
480 		case 'k':
481 			keys = 1;
482 			rp_id = optarg;
483 			break;
484 		case 'r':
485 			rplist = 1;
486 			break;
487 		default:
488 			break; /* ignore */
489 		}
490 	}
491 
492 	if (blobs || enrolls || keys || rplist) {
493 		if (path == NULL)
494 			usage();
495 		if (blobs)
496 			return (blob_list(path));
497 		if (enrolls)
498 			return (bio_list(path));
499 		if (keys)
500 			return (credman_list_rk(path, rp_id));
501 		if (rplist)
502 			return (credman_list_rp(path));
503 		/* NOTREACHED */
504 	}
505 
506 	if ((devlist = fido_dev_info_new(64)) == NULL)
507 		errx(1, "fido_dev_info_new");
508 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
509 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
510 
511 	for (size_t i = 0; i < ndevs; i++) {
512 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
513 		printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
514 		    fido_dev_info_path(di),
515 		    (uint16_t)fido_dev_info_vendor(di),
516 		    (uint16_t)fido_dev_info_product(di),
517 		    fido_dev_info_manufacturer_string(di),
518 		    fido_dev_info_product_string(di));
519 	}
520 
521 	fido_dev_info_free(&devlist, ndevs);
522 
523 	exit(0);
524 }
525 
526 int
527 token_delete(int argc, char **argv, char *path)
528 {
529 	char		*id = NULL;
530 	char		*key = NULL;
531 	char		*name = NULL;
532 	int		 blob = 0;
533 	int		 ch;
534 	int		 enroll = 0;
535 	int		 uv = 0;
536 
537 	optind = 1;
538 
539 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
540 		switch (ch) {
541 		case 'b':
542 			blob = 1;
543 			break;
544 		case 'e':
545 			enroll = 1;
546 			break;
547 		case 'i':
548 			id = optarg;
549 			break;
550 		case 'k':
551 			key = optarg;
552 			break;
553 		case 'n':
554 			name = optarg;
555 			break;
556 		case 'u':
557 			uv = 1;
558 			break;
559 		default:
560 			break; /* ignore */
561 		}
562 	}
563 
564 	if (path == NULL)
565 		usage();
566 
567 	if (blob)
568 		return (blob_delete(path, key, name, id));
569 
570 	if (id) {
571 		if (uv)
572 			usage();
573 		if (enroll == 0)
574 			return (credman_delete_rk(path, id));
575 		return (bio_delete(path, id));
576 	}
577 
578 	if (uv == 0)
579 		usage();
580 
581 	return (config_always_uv(path, 0));
582 }
583