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