1 /* 2 * Copyright (c) 2018-2021 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 <sys/types.h> 8 #include <sys/stat.h> 9 10 #include <openssl/ec.h> 11 #include <openssl/evp.h> 12 #include <openssl/pem.h> 13 14 #include <fido.h> 15 #include <fido/es256.h> 16 #include <fido/rs256.h> 17 #include <fido/eddsa.h> 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <limits.h> 22 #include <stdbool.h> 23 #include <stdint.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "../openbsd-compat/openbsd-compat.h" 30 #ifdef _MSC_VER 31 #include "../openbsd-compat/posix_win.h" 32 #endif 33 34 #include "extern.h" 35 36 char * 37 get_pin(const char *path) 38 { 39 char *pin; 40 char prompt[1024]; 41 int r, ok = -1; 42 43 if ((pin = calloc(1, PINBUF_LEN)) == NULL) { 44 warn("%s: calloc", __func__); 45 return NULL; 46 } 47 if ((r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", 48 path)) < 0 || (size_t)r >= sizeof(prompt)) { 49 warn("%s: snprintf", __func__); 50 goto out; 51 } 52 if (!readpassphrase(prompt, pin, PINBUF_LEN, RPP_ECHO_OFF)) { 53 warnx("%s: readpassphrase", __func__); 54 goto out; 55 } 56 57 ok = 0; 58 out: 59 if (ok < 0) { 60 freezero(pin, PINBUF_LEN); 61 pin = NULL; 62 } 63 64 return pin; 65 } 66 67 FILE * 68 open_write(const char *file) 69 { 70 int fd; 71 FILE *f; 72 73 if (file == NULL || strcmp(file, "-") == 0) 74 return (stdout); 75 if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0) 76 err(1, "open %s", file); 77 if ((f = fdopen(fd, "w")) == NULL) 78 err(1, "fdopen %s", file); 79 80 return (f); 81 } 82 83 FILE * 84 open_read(const char *file) 85 { 86 int fd; 87 FILE *f; 88 89 if (file == NULL || strcmp(file, "-") == 0) { 90 #ifdef FIDO_FUZZ 91 setvbuf(stdin, NULL, _IONBF, 0); 92 #endif 93 return (stdin); 94 } 95 if ((fd = open(file, O_RDONLY)) < 0) 96 err(1, "open %s", file); 97 if ((f = fdopen(fd, "r")) == NULL) 98 err(1, "fdopen %s", file); 99 100 return (f); 101 } 102 103 int 104 base10(const char *str) 105 { 106 char *ep; 107 long long ll; 108 109 ll = strtoll(str, &ep, 10); 110 if (str == ep || *ep != '\0') 111 return (-1); 112 else if (ll == LLONG_MIN && errno == ERANGE) 113 return (-1); 114 else if (ll == LLONG_MAX && errno == ERANGE) 115 return (-1); 116 else if (ll < 0 || ll > INT_MAX) 117 return (-1); 118 119 return ((int)ll); 120 } 121 122 void 123 xxd(const void *buf, size_t count) 124 { 125 const uint8_t *ptr = buf; 126 size_t i; 127 128 fprintf(stderr, " "); 129 130 for (i = 0; i < count; i++) { 131 fprintf(stderr, "%02x ", *ptr++); 132 if ((i + 1) % 16 == 0 && i + 1 < count) 133 fprintf(stderr, "\n "); 134 } 135 136 fprintf(stderr, "\n"); 137 fflush(stderr); 138 } 139 140 int 141 string_read(FILE *f, char **out) 142 { 143 char *line = NULL; 144 size_t linesize = 0; 145 ssize_t n; 146 147 *out = NULL; 148 149 if ((n = getline(&line, &linesize, f)) <= 0 || 150 (size_t)n != strlen(line)) { 151 free(line); 152 return (-1); 153 } 154 155 line[n - 1] = '\0'; /* trim \n */ 156 *out = line; 157 158 return (0); 159 } 160 161 fido_dev_t * 162 open_dev(const char *path) 163 { 164 fido_dev_t *dev; 165 int r; 166 167 if ((dev = fido_dev_new()) == NULL) 168 errx(1, "fido_dev_new"); 169 170 r = fido_dev_open(dev, path); 171 if (r != FIDO_OK) 172 errx(1, "fido_dev_open %s: %s", path, fido_strerr(r)); 173 174 return (dev); 175 } 176 177 int 178 get_devopt(fido_dev_t *dev, const char *name, int *val) 179 { 180 fido_cbor_info_t *cbor_info; 181 char * const *names; 182 const bool *values; 183 int r, ok = -1; 184 185 if ((cbor_info = fido_cbor_info_new()) == NULL) { 186 warnx("fido_cbor_info_new"); 187 goto out; 188 } 189 190 if ((r = fido_dev_get_cbor_info(dev, cbor_info)) != FIDO_OK) { 191 warnx("fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); 192 goto out; 193 } 194 195 if ((names = fido_cbor_info_options_name_ptr(cbor_info)) == NULL || 196 (values = fido_cbor_info_options_value_ptr(cbor_info)) == NULL) { 197 warnx("fido_dev_get_cbor_info: NULL name/value pointer"); 198 goto out; 199 } 200 201 *val = -1; 202 for (size_t i = 0; i < fido_cbor_info_options_len(cbor_info); i++) 203 if (strcmp(names[i], name) == 0) { 204 *val = values[i]; 205 break; 206 } 207 208 ok = 0; 209 out: 210 fido_cbor_info_free(&cbor_info); 211 212 return (ok); 213 } 214 215 EC_KEY * 216 read_ec_pubkey(const char *path) 217 { 218 FILE *fp = NULL; 219 EVP_PKEY *pkey = NULL; 220 EC_KEY *ec = NULL; 221 222 if ((fp = fopen(path, "r")) == NULL) { 223 warn("fopen"); 224 goto fail; 225 } 226 227 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { 228 warnx("PEM_read_PUBKEY"); 229 goto fail; 230 } 231 if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { 232 warnx("EVP_PKEY_get1_EC_KEY"); 233 goto fail; 234 } 235 236 fail: 237 if (fp) { 238 fclose(fp); 239 } 240 if (pkey) { 241 EVP_PKEY_free(pkey); 242 } 243 244 return (ec); 245 } 246 247 int 248 write_ec_pubkey(FILE *f, const void *ptr, size_t len) 249 { 250 EVP_PKEY *pkey = NULL; 251 es256_pk_t *pk = NULL; 252 int ok = -1; 253 254 if ((pk = es256_pk_new()) == NULL) { 255 warnx("es256_pk_new"); 256 goto fail; 257 } 258 259 if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { 260 warnx("es256_pk_from_ptr"); 261 goto fail; 262 } 263 264 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) { 265 warnx("es256_pk_to_EVP_PKEY"); 266 goto fail; 267 } 268 269 if (PEM_write_PUBKEY(f, pkey) == 0) { 270 warnx("PEM_write_PUBKEY"); 271 goto fail; 272 } 273 274 ok = 0; 275 fail: 276 es256_pk_free(&pk); 277 278 if (pkey != NULL) { 279 EVP_PKEY_free(pkey); 280 } 281 282 return (ok); 283 } 284 285 RSA * 286 read_rsa_pubkey(const char *path) 287 { 288 FILE *fp = NULL; 289 EVP_PKEY *pkey = NULL; 290 RSA *rsa = NULL; 291 292 if ((fp = fopen(path, "r")) == NULL) { 293 warn("fopen"); 294 goto fail; 295 } 296 297 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { 298 warnx("PEM_read_PUBKEY"); 299 goto fail; 300 } 301 if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) { 302 warnx("EVP_PKEY_get1_RSA"); 303 goto fail; 304 } 305 306 fail: 307 if (fp) { 308 fclose(fp); 309 } 310 if (pkey) { 311 EVP_PKEY_free(pkey); 312 } 313 314 return (rsa); 315 } 316 317 int 318 write_rsa_pubkey(FILE *f, const void *ptr, size_t len) 319 { 320 EVP_PKEY *pkey = NULL; 321 rs256_pk_t *pk = NULL; 322 int ok = -1; 323 324 if ((pk = rs256_pk_new()) == NULL) { 325 warnx("rs256_pk_new"); 326 goto fail; 327 } 328 329 if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) { 330 warnx("rs256_pk_from_ptr"); 331 goto fail; 332 } 333 334 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) { 335 warnx("rs256_pk_to_EVP_PKEY"); 336 goto fail; 337 } 338 339 if (PEM_write_PUBKEY(f, pkey) == 0) { 340 warnx("PEM_write_PUBKEY"); 341 goto fail; 342 } 343 344 ok = 0; 345 fail: 346 rs256_pk_free(&pk); 347 348 if (pkey != NULL) { 349 EVP_PKEY_free(pkey); 350 } 351 352 return (ok); 353 } 354 355 EVP_PKEY * 356 read_eddsa_pubkey(const char *path) 357 { 358 FILE *fp = NULL; 359 EVP_PKEY *pkey = NULL; 360 361 if ((fp = fopen(path, "r")) == NULL) { 362 warn("fopen"); 363 goto fail; 364 } 365 366 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { 367 warnx("PEM_read_PUBKEY"); 368 goto fail; 369 } 370 371 fail: 372 if (fp) { 373 fclose(fp); 374 } 375 376 return (pkey); 377 } 378 379 int 380 write_eddsa_pubkey(FILE *f, const void *ptr, size_t len) 381 { 382 EVP_PKEY *pkey = NULL; 383 eddsa_pk_t *pk = NULL; 384 int ok = -1; 385 386 if ((pk = eddsa_pk_new()) == NULL) { 387 warnx("eddsa_pk_new"); 388 goto fail; 389 } 390 391 if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) { 392 warnx("eddsa_pk_from_ptr"); 393 goto fail; 394 } 395 396 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) { 397 warnx("eddsa_pk_to_EVP_PKEY"); 398 goto fail; 399 } 400 401 if (PEM_write_PUBKEY(f, pkey) == 0) { 402 warnx("PEM_write_PUBKEY"); 403 goto fail; 404 } 405 406 ok = 0; 407 fail: 408 eddsa_pk_free(&pk); 409 410 if (pkey != NULL) { 411 EVP_PKEY_free(pkey); 412 } 413 414 return (ok); 415 } 416 417 void 418 print_cred(FILE *out_f, int type, const fido_cred_t *cred) 419 { 420 char *id; 421 int r; 422 423 r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id); 424 if (r < 0) 425 errx(1, "output error"); 426 427 fprintf(out_f, "%s\n", id); 428 429 if (type == COSE_ES256) { 430 write_ec_pubkey(out_f, fido_cred_pubkey_ptr(cred), 431 fido_cred_pubkey_len(cred)); 432 } else if (type == COSE_RS256) { 433 write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), 434 fido_cred_pubkey_len(cred)); 435 } else if (type == COSE_EDDSA) { 436 write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred), 437 fido_cred_pubkey_len(cred)); 438 } else { 439 errx(1, "print_cred: unknown type"); 440 } 441 442 free(id); 443 } 444 445 int 446 cose_type(const char *str, int *type) 447 { 448 if (strcmp(str, "es256") == 0) 449 *type = COSE_ES256; 450 else if (strcmp(str, "rs256") == 0) 451 *type = COSE_RS256; 452 else if (strcmp(str, "eddsa") == 0) 453 *type = COSE_EDDSA; 454 else { 455 *type = 0; 456 return (-1); 457 } 458 459 return (0); 460 } 461 462 const char * 463 cose_string(int type) 464 { 465 switch (type) { 466 case COSE_EDDSA: 467 return ("eddsa"); 468 case COSE_ES256: 469 return ("es256"); 470 case COSE_RS256: 471 return ("rs256"); 472 default: 473 return ("unknown"); 474 } 475 } 476 477 const char * 478 prot_string(int prot) 479 { 480 switch (prot) { 481 case FIDO_CRED_PROT_UV_OPTIONAL: 482 return ("uvopt"); 483 case FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID: 484 return ("uvopt+id"); 485 case FIDO_CRED_PROT_UV_REQUIRED: 486 return ("uvreq"); 487 default: 488 return ("unknown"); 489 } 490 } 491 492 int 493 read_file(const char *path, u_char **ptr, size_t *len) 494 { 495 int fd, ok = -1; 496 struct stat st; 497 ssize_t n; 498 499 *ptr = NULL; 500 *len = 0; 501 502 if ((fd = open(path, O_RDONLY)) < 0) { 503 warn("%s: open %s", __func__, path); 504 goto fail; 505 } 506 if (fstat(fd, &st) < 0) { 507 warn("%s: stat %s", __func__, path); 508 goto fail; 509 } 510 if (st.st_size < 0) { 511 warnx("%s: stat %s: invalid size", __func__, path); 512 goto fail; 513 } 514 *len = (size_t)st.st_size; 515 if ((*ptr = malloc(*len)) == NULL) { 516 warn("%s: malloc", __func__); 517 goto fail; 518 } 519 if ((n = read(fd, *ptr, *len)) < 0) { 520 warn("%s: read", __func__); 521 goto fail; 522 } 523 if ((size_t)n != *len) { 524 warnx("%s: read", __func__); 525 goto fail; 526 } 527 528 ok = 0; 529 fail: 530 if (fd != -1) { 531 close(fd); 532 } 533 if (ok < 0) { 534 free(*ptr); 535 *ptr = NULL; 536 *len = 0; 537 } 538 539 return ok; 540 } 541 542 int 543 write_file(const char *path, const u_char *ptr, size_t len) 544 { 545 int fd, ok = -1; 546 ssize_t n; 547 548 if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) { 549 warn("%s: open %s", __func__, path); 550 goto fail; 551 } 552 if ((n = write(fd, ptr, len)) < 0) { 553 warn("%s: write", __func__); 554 goto fail; 555 } 556 if ((size_t)n != len) { 557 warnx("%s: write", __func__); 558 goto fail; 559 } 560 561 ok = 0; 562 fail: 563 if (fd != -1) { 564 close(fd); 565 } 566 567 return ok; 568 } 569 570 const char * 571 plural(size_t x) 572 { 573 return x == 1 ? "" : "s"; 574 } 575 576 int 577 should_retry_with_pin(const fido_dev_t *dev, int r) 578 { 579 if (fido_dev_has_pin(dev) == false) { 580 return 0; 581 } 582 583 switch (r) { 584 case FIDO_ERR_PIN_REQUIRED: 585 case FIDO_ERR_UNAUTHORIZED_PERM: 586 case FIDO_ERR_UV_BLOCKED: 587 case FIDO_ERR_UV_INVALID: 588 return 1; 589 } 590 591 return 0; 592 } 593