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_maxcredcntlst(uint64_t maxcredcntlst) 116 { 117 printf("maxcredcntlst: %d\n", (int)maxcredcntlst); 118 } 119 120 static void 121 print_maxcredidlen(uint64_t maxcredidlen) 122 { 123 printf("maxcredlen: %d\n", (int)maxcredidlen); 124 } 125 126 static void 127 print_fwversion(uint64_t fwversion) 128 { 129 printf("fwversion: 0x%x\n", (int)fwversion); 130 } 131 132 static void 133 print_byte_array(const char *label, const uint8_t *ba, size_t len) 134 { 135 if (len == 0) 136 return; 137 138 printf("%s: ", label); 139 140 for (size_t i = 0; i < len; i++) 141 printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]); 142 143 printf("\n"); 144 } 145 146 int 147 token_info(int argc, char **argv, char *path) 148 { 149 char *cred_id = NULL; 150 char *rp_id = NULL; 151 fido_cbor_info_t *ci = NULL; 152 fido_dev_t *dev = NULL; 153 int ch; 154 int credman = 0; 155 int r; 156 int retrycnt; 157 158 optind = 1; 159 160 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { 161 switch (ch) { 162 case 'c': 163 credman = 1; 164 break; 165 case 'i': 166 cred_id = optarg; 167 break; 168 case 'k': 169 rp_id = optarg; 170 break; 171 default: 172 break; /* ignore */ 173 } 174 } 175 176 if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL))) 177 usage(); 178 179 dev = open_dev(path); 180 181 if (credman) 182 return (credman_get_metadata(dev, path)); 183 if (cred_id && rp_id) 184 return (credman_print_rk(dev, path, rp_id, cred_id)); 185 if (cred_id || rp_id) 186 usage(); 187 188 print_attr(dev); 189 190 if (fido_dev_is_fido2(dev) == false) 191 goto end; 192 if ((ci = fido_cbor_info_new()) == NULL) 193 errx(1, "fido_cbor_info_new"); 194 if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK) 195 errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); 196 197 /* print supported protocol versions */ 198 print_str_array("version", fido_cbor_info_versions_ptr(ci), 199 fido_cbor_info_versions_len(ci)); 200 201 /* print supported extensions */ 202 print_str_array("extension", fido_cbor_info_extensions_ptr(ci), 203 fido_cbor_info_extensions_len(ci)); 204 205 /* print aaguid */ 206 print_aaguid(fido_cbor_info_aaguid_ptr(ci), 207 fido_cbor_info_aaguid_len(ci)); 208 209 /* print supported options */ 210 print_opt_array("options", fido_cbor_info_options_name_ptr(ci), 211 fido_cbor_info_options_value_ptr(ci), 212 fido_cbor_info_options_len(ci)); 213 214 /* print maximum message size */ 215 print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci)); 216 217 /* print maximum number of credentials allowed in credential lists */ 218 print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci)); 219 220 /* print maximum length of a credential ID */ 221 print_maxcredidlen(fido_cbor_info_maxcredidlen(ci)); 222 223 /* print firmware version */ 224 print_fwversion(fido_cbor_info_fwversion(ci)); 225 226 /* print supported pin protocols */ 227 print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), 228 fido_cbor_info_protocols_len(ci)); 229 230 if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK) 231 printf("pin retries: undefined\n"); 232 else 233 printf("pin retries: %d\n", retrycnt); 234 235 bio_info(dev); 236 237 fido_cbor_info_free(&ci); 238 end: 239 fido_dev_close(dev); 240 fido_dev_free(&dev); 241 242 exit(0); 243 } 244 245 int 246 token_reset(char *path) 247 { 248 fido_dev_t *dev = NULL; 249 int r; 250 251 if (path == NULL) 252 usage(); 253 254 dev = open_dev(path); 255 if ((r = fido_dev_reset(dev)) != FIDO_OK) 256 errx(1, "fido_dev_reset: %s", fido_strerr(r)); 257 258 fido_dev_close(dev); 259 fido_dev_free(&dev); 260 261 exit(0); 262 } 263 264 int 265 token_set(int argc, char **argv, char *path) 266 { 267 char *id = NULL; 268 char *name = NULL; 269 int ch; 270 int enroll = 0; 271 272 optind = 1; 273 274 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { 275 switch (ch) { 276 case 'e': 277 enroll = 1; 278 break; 279 case 'i': 280 id = optarg; 281 break; 282 case 'n': 283 name = optarg; 284 break; 285 default: 286 break; /* ignore */ 287 } 288 } 289 290 if (enroll) { 291 if (id && name) 292 return (bio_set_name(path, id, name)); 293 if (!id && !name) 294 return (bio_enroll(path)); 295 usage(); 296 } 297 298 return (pin_set(path)); 299 } 300 301 int 302 token_list(int argc, char **argv, char *path) 303 { 304 fido_dev_info_t *devlist; 305 size_t ndevs; 306 const char *rp_id = NULL; 307 int enrolls = 0; 308 int keys = 0; 309 int rplist = 0; 310 int ch; 311 int r; 312 313 optind = 1; 314 315 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { 316 switch (ch) { 317 case 'e': 318 enrolls = 1; 319 break; 320 case 'k': 321 keys = 1; 322 rp_id = optarg; 323 break; 324 case 'r': 325 rplist = 1; 326 break; 327 default: 328 break; /* ignore */ 329 } 330 } 331 332 if (enrolls) 333 return (bio_list(path)); 334 if (keys) 335 return (credman_list_rk(path, rp_id)); 336 if (rplist) 337 return (credman_list_rp(path)); 338 339 if ((devlist = fido_dev_info_new(64)) == NULL) 340 errx(1, "fido_dev_info_new"); 341 if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) 342 errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); 343 344 for (size_t i = 0; i < ndevs; i++) { 345 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); 346 printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n", 347 fido_dev_info_path(di), 348 (uint16_t)fido_dev_info_vendor(di), 349 (uint16_t)fido_dev_info_product(di), 350 fido_dev_info_manufacturer_string(di), 351 fido_dev_info_product_string(di)); 352 } 353 354 fido_dev_info_free(&devlist, ndevs); 355 356 exit(0); 357 } 358 359 int 360 token_delete(int argc, char **argv, char *path) 361 { 362 char *id = NULL; 363 fido_dev_t *dev = NULL; 364 int ch; 365 int enroll = 0; 366 367 optind = 1; 368 369 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { 370 switch (ch) { 371 case 'e': 372 enroll = 1; 373 break; 374 case 'i': 375 id = optarg; 376 break; 377 default: 378 break; /* ignore */ 379 } 380 } 381 382 if (path == NULL || id == NULL) 383 usage(); 384 385 dev = open_dev(path); 386 387 if (id && !enroll) 388 return (credman_delete_rk(dev, path, id)); 389 390 return (bio_delete(dev, path, id)); 391 } 392