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