1 /* 2 * Copyright (C) 2014-2019 Yubico AB - See COPYING 3 */ 4 5 #include <fido.h> 6 #include <fido/es256.h> 7 #include <fido/rs256.h> 8 #include <fido/eddsa.h> 9 10 #include <openssl/ec.h> 11 #include <openssl/obj_mac.h> 12 13 #include <inttypes.h> 14 #include <limits.h> 15 #include <stdlib.h> 16 #include <fcntl.h> 17 #include <sys/stat.h> 18 #include <stdarg.h> 19 #include <syslog.h> 20 #include <pwd.h> 21 #include <errno.h> 22 #include <unistd.h> 23 #include <string.h> 24 #include <arpa/inet.h> 25 26 #include "b64.h" 27 #include "util.h" 28 29 #define SSH_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n" 30 #define SSH_HEADER_LEN (sizeof(SSH_HEADER) - 1) 31 #define SSH_TRAILER "-----END OPENSSH PRIVATE KEY-----\n" 32 #define SSH_TRAILER_LEN (sizeof(SSH_TRAILER) - 1) 33 #define SSH_AUTH_MAGIC "openssh-key-v1" 34 #define SSH_AUTH_MAGIC_LEN (sizeof(SSH_AUTH_MAGIC)) // AUTH_MAGIC includes \0 35 #define SSH_ES256 "sk-ecdsa-sha2-nistp256@openssh.com" 36 #define SSH_ES256_LEN (sizeof(SSH_ES256) - 1) 37 #define SSH_ES256_POINT_LEN 65 38 #define SSH_P256_NAME "nistp256" 39 #define SSH_P256_NAME_LEN (sizeof(SSH_P256_NAME) - 1) 40 #define SSH_EDDSA "sk-ssh-ed25519@openssh.com" 41 #define SSH_EDDSA_LEN (sizeof(SSH_EDDSA) - 1) 42 #define SSH_EDDSA_POINT_LEN 32 43 #define SSH_SK_USER_PRESENCE_REQD 0x01 44 #define SSH_SK_USER_VERIFICATION_REQD 0x04 45 #define SSH_SK_RESIDENT_KEY 0x20 46 47 struct opts { 48 fido_opt_t up; 49 fido_opt_t uv; 50 fido_opt_t pin; 51 }; 52 53 struct pk { 54 void *ptr; 55 int type; 56 }; 57 58 static int hex_decode(const char *ascii_hex, unsigned char **blob, 59 size_t *blob_len) { 60 *blob = NULL; 61 *blob_len = 0; 62 63 if (ascii_hex == NULL || (strlen(ascii_hex) % 2) != 0) 64 return (0); 65 66 *blob_len = strlen(ascii_hex) / 2; 67 *blob = calloc(1, *blob_len); 68 if (*blob == NULL) 69 return (0); 70 71 for (size_t i = 0; i < *blob_len; i++) { 72 unsigned int c; 73 int n = -1; 74 int r = sscanf(ascii_hex, "%02x%n", &c, &n); 75 if (r != 1 || n != 2 || c > UCHAR_MAX) { 76 free(*blob); 77 *blob = NULL; 78 *blob_len = 0; 79 return (0); 80 } 81 (*blob)[i] = (unsigned char) c; 82 ascii_hex += n; 83 } 84 85 return (1); 86 } 87 88 static char *normal_b64(const char *websafe_b64) { 89 char *b64; 90 char *p; 91 size_t n; 92 93 n = strlen(websafe_b64); 94 if (n > SIZE_MAX - 3) 95 return (NULL); 96 97 b64 = calloc(1, n + 3); 98 if (b64 == NULL) 99 return (NULL); 100 101 memcpy(b64, websafe_b64, n); 102 p = b64; 103 104 while ((p = strpbrk(p, "-_")) != NULL) { 105 switch (*p) { 106 case '-': 107 *p++ = '+'; 108 break; 109 case '_': 110 *p++ = '/'; 111 break; 112 } 113 } 114 115 switch (n % 4) { 116 case 1: 117 b64[n] = '='; 118 break; 119 case 2: 120 case 3: 121 b64[n] = '='; 122 b64[n + 1] = '='; 123 break; 124 } 125 126 return (b64); 127 } 128 129 static int translate_old_format_pubkey(es256_pk_t *es256_pk, 130 const unsigned char *pk, size_t pk_len) { 131 EC_KEY *ec = NULL; 132 EC_POINT *q = NULL; 133 const EC_GROUP *g = NULL; 134 int r = FIDO_ERR_INTERNAL; 135 136 if (es256_pk == NULL) 137 goto fail; 138 139 if ((ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || 140 (g = EC_KEY_get0_group(ec)) == NULL) 141 goto fail; 142 143 if ((q = EC_POINT_new(g)) == NULL || 144 !EC_POINT_oct2point(g, q, pk, pk_len, NULL) || 145 !EC_KEY_set_public_key(ec, q)) 146 goto fail; 147 148 r = es256_pk_from_EC_KEY(es256_pk, ec); 149 150 fail: 151 if (ec != NULL) 152 EC_KEY_free(ec); 153 if (q != NULL) 154 EC_POINT_free(q); 155 156 return r; 157 } 158 159 static int is_resident(const char *kh) { return strcmp(kh, "*") == 0; } 160 161 static void reset_device(device_t *device) { 162 free(device->keyHandle); 163 free(device->publicKey); 164 free(device->coseType); 165 free(device->attributes); 166 memset(device, 0, sizeof(*device)); 167 } 168 169 static int parse_native_format(const cfg_t *cfg, const char *username, 170 char *buf, FILE *opwfile, device_t *devices, 171 unsigned *n_devs) { 172 173 char *s_user, *s_credential; 174 const char *s_token; 175 unsigned i; 176 177 while (fgets(buf, (int) (DEVSIZE * (cfg->max_devs - 1)), opwfile)) { 178 char *saveptr = NULL; 179 size_t len = strlen(buf); 180 if (len > 0 && buf[len - 1] == '\n') 181 buf[len - 1] = '\0'; 182 183 if (cfg->debug) 184 D(cfg->debug_file, "Authorization line: %s", buf); 185 186 s_user = strtok_r(buf, ":", &saveptr); 187 if (s_user && strcmp(username, s_user) == 0) { 188 if (cfg->debug) 189 D(cfg->debug_file, "Matched user: %s", s_user); 190 191 // only keep last line for this user 192 for (i = 0; i < *n_devs; i++) { 193 reset_device(&devices[i]); 194 } 195 *n_devs = 0; 196 197 i = 0; 198 while ((s_credential = strtok_r(NULL, ":", &saveptr)) != NULL) { 199 // s_credential is the whole line now 200 char *credsaveptr = NULL; 201 202 if ((*n_devs)++ > cfg->max_devs - 1) { 203 *n_devs = cfg->max_devs; 204 if (cfg->debug) { 205 D(cfg->debug_file, 206 "Found more than %d devices, ignoring the remaining ones", 207 cfg->max_devs); 208 } 209 break; 210 } 211 212 reset_device(&devices[i]); 213 214 s_token = strtok_r(s_credential, ",", &credsaveptr); 215 216 if (!s_token) { 217 if (cfg->debug) { 218 D(cfg->debug_file, 219 "Unable to retrieve keyHandle for device %d", i + 1); 220 } 221 return -1; 222 } 223 224 if (cfg->debug) { 225 D(cfg->debug_file, "KeyHandle for device number %d: %s", i + 1, 226 s_token); 227 } 228 229 devices[i].keyHandle = strdup(s_token); 230 231 if (!devices[i].keyHandle) { 232 if (cfg->debug) { 233 D(cfg->debug_file, 234 "Unable to allocate memory for keyHandle number %d", i); 235 } 236 return -1; 237 } 238 239 if (is_resident(devices[i].keyHandle) && cfg->debug) { 240 D(cfg->debug_file, "Credential is resident"); 241 } 242 243 s_token = strtok_r(NULL, ",", &credsaveptr); 244 245 if (!s_token) { 246 if (cfg->debug) { 247 D(cfg->debug_file, "Unable to retrieve publicKey number %d", i + 1); 248 } 249 return -1; 250 } 251 252 if (cfg->debug) { 253 D(cfg->debug_file, "publicKey for device number %d: %s", i + 1, 254 s_token); 255 } 256 257 devices[i].publicKey = strdup(s_token); 258 259 if (!devices[i].publicKey) { 260 if (cfg->debug) { 261 D(cfg->debug_file, 262 "Unable to allocate memory for publicKey number %d", i); 263 } 264 return -1; 265 } 266 267 s_token = strtok_r(NULL, ",", &credsaveptr); 268 269 if (!s_token) { 270 if (cfg->debug) { 271 D(cfg->debug_file, "Unable to retrieve COSE type %d", i + 1); 272 D(cfg->debug_file, "Assuming ES256 (backwards compatibility)"); 273 } 274 devices[i].old_format = 1; 275 devices[i].coseType = strdup("es256"); 276 } else { 277 if (cfg->debug) { 278 D(cfg->debug_file, "COSE type for device number %d: %s", i + 1, 279 s_token); 280 } 281 devices[i].coseType = strdup(s_token); 282 } 283 284 if (!devices[i].coseType) { 285 if (cfg->debug) { 286 D(cfg->debug_file, 287 "Unable to allocate memory for COSE type number %d", i); 288 } 289 return -1; 290 } 291 292 s_token = strtok_r(NULL, ",", &credsaveptr); 293 294 if (devices[i].old_format == 1) { 295 if (cfg->debug) { 296 D(cfg->debug_file, "Old format for device %d, no attributes", 297 i + 1); 298 D(cfg->debug_file, "Assuming 'presence' (backwards compatibility)"); 299 } 300 s_token = "+presence"; 301 } else if (!s_token) { 302 s_token = ""; 303 } 304 305 if (cfg->debug) { 306 D(cfg->debug_file, "Attributes for device number %d: %s", i + 1, 307 s_token); 308 } 309 devices[i].attributes = strdup(s_token); 310 311 if (!devices[i].attributes) { 312 if (cfg->debug) { 313 D(cfg->debug_file, 314 "Unable to allocate memory for attributes number %d", i); 315 } 316 return -1; 317 } 318 319 if (devices[i].old_format) { 320 char *websafe_b64 = devices[i].keyHandle; 321 devices[i].keyHandle = normal_b64(websafe_b64); 322 free(websafe_b64); 323 if (!devices[i].keyHandle) { 324 if (cfg->debug) { 325 D(cfg->debug_file, 326 "Unable to allocate memory for keyHandle number %d", i); 327 } 328 return -1; 329 } 330 } 331 332 i++; 333 } 334 } 335 } 336 337 return 1; 338 } 339 340 static int load_ssh_key(const cfg_t *cfg, char *buf, size_t buf_size, 341 FILE *opwfile, size_t opwfile_size) { 342 char *cp = buf; 343 int ch; 344 345 if (opwfile_size > buf_size || 346 opwfile_size < SSH_HEADER_LEN + SSH_TRAILER_LEN) { 347 if (cfg->debug) { 348 D(cfg->debug_file, "Malformed SSH key (length)"); 349 } 350 return 0; 351 } 352 353 // NOTE(adma): +1 for \0 354 if (fgets(buf, (int)(SSH_HEADER_LEN + 1), opwfile) == NULL || 355 strlen(buf) != SSH_HEADER_LEN || 356 strncmp(buf, SSH_HEADER, SSH_HEADER_LEN) != 0) { 357 if (cfg->debug) { 358 D(cfg->debug_file, "Malformed SSH key (header)"); 359 } 360 return 0; 361 } 362 363 while (opwfile_size > 0 && buf_size > 1) { 364 ch = fgetc(opwfile); 365 if (ch == EOF) { 366 if (cfg->debug) { 367 D(cfg->debug_file, "Unexpected authfile termination"); 368 } 369 return 0; 370 } 371 372 opwfile_size--; 373 buf_size--; 374 375 if (ch != '\n' && ch != '\r') { 376 *cp = (char) ch; 377 if (ch == '-') { 378 // NOTE(adma): no +1 here since we already read one '-' 379 if (buf_size < SSH_TRAILER_LEN || 380 fgets(cp + 1, (int)SSH_TRAILER_LEN, opwfile) == NULL || 381 strlen(cp) != SSH_TRAILER_LEN || 382 strncmp(cp, SSH_TRAILER, SSH_TRAILER_LEN) != 0) { 383 if (cfg->debug) { 384 D(cfg->debug_file, "Malformed SSH key (trailer)"); 385 } 386 return 0; 387 } 388 389 *(cp) = '\0'; 390 break; 391 } else { 392 cp++; 393 } 394 } 395 } 396 397 if (cfg->debug) { // TODO(adma): too verbose? Delete? 398 D(cfg->debug_file, "Credential is \"%s\"", buf); 399 } 400 401 return 1; 402 } 403 404 static int ssh_get(const unsigned char **buf, size_t *size, unsigned char *dst, 405 size_t len) { 406 if (*size < len) 407 return 0; 408 if (dst != NULL) 409 memcpy(dst, *buf, len); 410 *buf += len; 411 *size -= len; 412 return 1; 413 } 414 415 static int ssh_get_u8(const unsigned char **buf, size_t *size, uint8_t *val) { 416 return ssh_get(buf, size, val, sizeof(*val)); 417 } 418 419 static int ssh_get_u32(const unsigned char **buf, size_t *size, uint32_t *val) { 420 if (!ssh_get(buf, size, (unsigned char *) val, sizeof(*val))) 421 return 0; 422 if (val != NULL) 423 *val = ntohl(*val); 424 return 1; 425 } 426 427 static int ssh_get_string_ref(const unsigned char **buf, size_t *size, 428 const unsigned char **ref, size_t *lenp) { 429 uint32_t len; 430 431 if (!ssh_get_u32(buf, size, &len)) 432 return 0; 433 if (!ssh_get(buf, size, NULL, len)) 434 return 0; 435 if (ref != NULL) 436 *ref = *buf - len; 437 if (lenp != NULL) 438 *lenp = len; 439 return 1; 440 } 441 442 static int ssh_get_cstring(const unsigned char **buf, size_t *size, char **str, 443 size_t *lenp) { 444 const unsigned char *ref; 445 size_t len; 446 447 if (!ssh_get_string_ref(buf, size, &ref, &len)) 448 return 0; 449 if (str != NULL) { 450 if (len > SIZE_MAX - 1 || (*str = calloc(1, len + 1)) == NULL) 451 return 0; 452 memcpy(*str, ref, len); 453 } 454 if (lenp != NULL) 455 *lenp = len; 456 return 1; 457 } 458 459 static int ssh_log_cstring(const cfg_t *cfg, const unsigned char **buf, 460 size_t *size, const char *name) { 461 char *str = NULL; 462 size_t len; 463 464 (void) name; // silence compiler warnings if PAM_DEBUG disabled 465 466 if (!ssh_get_cstring(buf, size, &str, &len)) { 467 if (cfg->debug) 468 D(cfg->debug_file, "Malformed SSH key (%s)", name); 469 return 0; 470 } 471 if (cfg->debug) 472 D(cfg->debug_file, "%s (%zu) \"%s\"", name, len, str); 473 474 free(str); 475 return 1; 476 } 477 478 static int ssh_get_attrs(const cfg_t *cfg, const unsigned char **buf, 479 size_t *size, char **attrs) { 480 char tmp[32] = {0}; 481 uint8_t flags; 482 int r; 483 484 // flags 485 if (!ssh_get_u8(buf, size, &flags)) { 486 if (cfg->debug) { 487 D(cfg->debug_file, "Malformed SSH key (flags)"); 488 } 489 return 0; 490 } 491 if (cfg->debug) { 492 D(cfg->debug_file, "flags: %02x", flags); 493 } 494 495 r = snprintf(tmp, sizeof(tmp), "%s%s", 496 flags & SSH_SK_USER_PRESENCE_REQD ? "+presence" : "", 497 flags & SSH_SK_USER_VERIFICATION_REQD ? "+verification" : ""); 498 if (r < 0 || (size_t) r >= sizeof(tmp)) { 499 if (cfg->debug) 500 D(cfg->debug_file, "Unable to prepare flags"); 501 return 0; 502 } 503 504 if ((*attrs = strdup(tmp)) == NULL) { 505 if (cfg->debug) { 506 D(cfg->debug_file, "Unable to allocate attributes"); 507 } 508 return 0; 509 } 510 511 return 1; 512 } 513 514 static int ssh_get_pubkey(const cfg_t *cfg, const unsigned char **buf, 515 size_t *size, char **type_p, char **pubkey_p) { 516 char *ssh_type = NULL; 517 char *ssh_curve = NULL; 518 const unsigned char *blob; 519 size_t len; 520 int type; 521 size_t point_len; 522 int ok = 0; 523 524 *type_p = NULL; 525 *pubkey_p = NULL; 526 527 // key type 528 if (!ssh_get_cstring(buf, size, &ssh_type, &len)) { 529 if (cfg->debug) { 530 D(cfg->debug_file, "Malformed SSH key (keytype)"); 531 } 532 goto err; 533 } 534 535 if (len == SSH_ES256_LEN && memcmp(ssh_type, SSH_ES256, SSH_ES256_LEN) == 0) { 536 type = COSE_ES256; 537 point_len = SSH_ES256_POINT_LEN; 538 } else if (len == SSH_EDDSA_LEN && 539 memcmp(ssh_type, SSH_EDDSA, SSH_EDDSA_LEN) == 0) { 540 type = COSE_EDDSA; 541 point_len = SSH_EDDSA_POINT_LEN; 542 } else { 543 if (cfg->debug) { 544 D(cfg->debug_file, "Unknown key type %s", ssh_type); 545 } 546 goto err; 547 } 548 549 if (cfg->debug) { 550 D(cfg->debug_file, "keytype (%zu) \"%s\"", len, ssh_type); 551 } 552 553 if (type == COSE_ES256) { 554 // curve name 555 if (!ssh_get_cstring(buf, size, &ssh_curve, &len)) { 556 if (cfg->debug) { 557 D(cfg->debug_file, "Malformed SSH key (curvename)"); 558 } 559 goto err; 560 } 561 562 if (len == SSH_P256_NAME_LEN && 563 memcmp(ssh_curve, SSH_P256_NAME, SSH_P256_NAME_LEN) == 0) { 564 if (cfg->debug) { 565 D(cfg->debug_file, "curvename (%zu) \"%s\"", len, ssh_curve); 566 } 567 } else { 568 if (cfg->debug) { 569 D(cfg->debug_file, "Unknown curve %s", ssh_curve); 570 } 571 goto err; 572 } 573 } 574 575 // point 576 if (!ssh_get_string_ref(buf, size, &blob, &len)) { 577 if (cfg->debug) { 578 D(cfg->debug_file, "Malformed SSH key (point)"); 579 } 580 goto err; 581 } 582 583 if (len != point_len) { 584 if (cfg->debug) { 585 D(cfg->debug_file, "Invalid point length, should be %zu, found %zu", 586 point_len, len); 587 } 588 goto err; 589 } 590 591 if (type == COSE_ES256) { 592 // Skip the initial '04' 593 if (len < 1) { 594 if (cfg->debug) { 595 D(cfg->debug_file, "Failed to skip initial '04'"); 596 } 597 goto err; 598 } 599 blob++; 600 len--; 601 } 602 603 if (!b64_encode(blob, len, pubkey_p)) { 604 if (cfg->debug) { 605 D(cfg->debug_file, "Unable to allocate public key"); 606 } 607 goto err; 608 } 609 610 if ((*type_p = strdup(cose_string(type))) == NULL) { 611 if (cfg->debug) { 612 D(cfg->debug_file, "Unable to allocate COSE type"); 613 } 614 goto err; 615 } 616 617 ok = 1; 618 err: 619 if (!ok) { 620 free(*type_p); 621 free(*pubkey_p); 622 *type_p = NULL; 623 *pubkey_p = NULL; 624 } 625 free(ssh_type); 626 free(ssh_curve); 627 628 return ok; 629 } 630 631 static int parse_ssh_format(const cfg_t *cfg, char *buf, size_t buf_size, 632 FILE *opwfile, size_t opwfile_size, 633 device_t *devices, unsigned *n_devs) { 634 635 const unsigned char *decoded; 636 unsigned char *decoded_initial = NULL; 637 size_t decoded_len; 638 const unsigned char *blob; 639 uint32_t check1, check2, tmp; 640 size_t len; 641 642 // The logic below is inspired by 643 // how ssh parses its own keys. See sshkey.c 644 reset_device(&devices[0]); 645 646 if (!load_ssh_key(cfg, buf, buf_size, opwfile, opwfile_size) || 647 !b64_decode(buf, (void **) &decoded_initial, &decoded_len)) { 648 if (cfg->debug) { 649 D(cfg->debug_file, "Unable to decode credential"); 650 } 651 goto out; 652 } 653 654 decoded = decoded_initial; 655 656 // magic 657 if (decoded_len < SSH_AUTH_MAGIC_LEN || 658 memcmp(decoded, SSH_AUTH_MAGIC, SSH_AUTH_MAGIC_LEN) != 0) { 659 if (cfg->debug) { 660 D(cfg->debug_file, "Malformed SSH key (magic)"); 661 } 662 goto out; 663 } 664 665 decoded += SSH_AUTH_MAGIC_LEN; 666 decoded_len -= SSH_AUTH_MAGIC_LEN; 667 668 if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "ciphername") || 669 !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfname") || 670 !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfoptions")) 671 goto out; 672 673 if (!ssh_get_u32(&decoded, &decoded_len, &tmp)) { 674 if (cfg->debug) { 675 D(cfg->debug_file, "Malformed SSH key (nkeys)"); 676 } 677 goto out; 678 } 679 if (cfg->debug) { 680 D(cfg->debug_file, "nkeys: %" PRIu32, tmp); 681 } 682 if (tmp != 1) { 683 if (cfg->debug) { 684 D(cfg->debug_file, "Multiple keys not supported"); 685 } 686 goto out; 687 } 688 689 // public_key (skip) 690 if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) { 691 if (cfg->debug) { 692 D(cfg->debug_file, "Malformed SSH key (pubkey)"); 693 } 694 goto out; 695 } 696 697 // private key (consume length) 698 if (!ssh_get_u32(&decoded, &decoded_len, &tmp) || decoded_len < tmp) { 699 if (cfg->debug) { 700 D(cfg->debug_file, "Malformed SSH key (pvtkey length)"); 701 } 702 goto out; 703 } 704 705 // check1, check2 706 if (!ssh_get_u32(&decoded, &decoded_len, &check1) || 707 !ssh_get_u32(&decoded, &decoded_len, &check2)) { 708 if (cfg->debug) { 709 D(cfg->debug_file, "Malformed SSH key (check1, check2)"); 710 } 711 goto out; 712 } 713 if (cfg->debug) { 714 D(cfg->debug_file, "check1: %" PRIu32, check1); 715 D(cfg->debug_file, "check2: %" PRIu32, check2); 716 } 717 if (check1 != check2) { 718 if (cfg->debug) { 719 D(cfg->debug_file, "Mismatched check values"); 720 goto out; 721 } 722 } 723 724 if (!ssh_get_pubkey(cfg, &decoded, &decoded_len, &devices[0].coseType, 725 &devices[0].publicKey) || 726 !ssh_log_cstring(cfg, &decoded, &decoded_len, "application") || 727 !ssh_get_attrs(cfg, &decoded, &decoded_len, &devices[0].attributes)) 728 goto out; 729 730 // keyhandle 731 if (!ssh_get_string_ref(&decoded, &decoded_len, &blob, &len) || 732 !b64_encode(blob, len, &devices[0].keyHandle)) { 733 if (cfg->debug) { 734 D(cfg->debug_file, "Malformed SSH key (keyhandle)"); 735 } 736 goto out; 737 } 738 739 if (cfg->debug) { 740 D(cfg->debug_file, "KeyHandle for device number 1: %s", 741 devices[0].keyHandle); 742 D(cfg->debug_file, "publicKey for device number 1: %s", 743 devices[0].publicKey); 744 D(cfg->debug_file, "COSE type for device number 1: %s", 745 devices[0].coseType); 746 D(cfg->debug_file, "Attributes for device number 1: %s", 747 devices[0].attributes); 748 } 749 750 // reserved (skip) 751 if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) { 752 if (cfg->debug) { 753 D(cfg->debug_file, "Malformed SSH key (reserved)"); 754 } 755 goto out; 756 } 757 758 // comment 759 if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "comment")) 760 goto out; 761 762 // padding 763 if (decoded_len >= 255) { 764 if (cfg->debug) { 765 D(cfg->debug_file, "Malformed SSH key (padding length)"); 766 } 767 goto out; 768 } 769 770 for (int i = 1; (unsigned) i <= decoded_len; i++) { 771 if (decoded[i - 1] != i) { 772 if (cfg->debug) { 773 D(cfg->debug_file, "Malformed SSH key (padding)"); 774 } 775 goto out; 776 } 777 } 778 779 free(decoded_initial); 780 decoded_initial = NULL; 781 782 *n_devs = 1; 783 784 return 1; 785 786 out: 787 reset_device(&devices[0]); 788 789 if (decoded_initial) { 790 free(decoded_initial); 791 decoded_initial = NULL; 792 } 793 794 return -1; 795 } 796 797 int get_devices_from_authfile(const cfg_t *cfg, const char *username, 798 device_t *devices, unsigned *n_devs) { 799 800 char *buf = NULL; 801 int retval = 0; 802 int fd = -1; 803 struct stat st; 804 struct passwd *pw = NULL, pw_s; 805 char buffer[BUFSIZE]; 806 int gpu_ret; 807 FILE *opwfile = NULL; 808 size_t opwfile_size; 809 unsigned i; 810 811 /* Ensure we never return uninitialized count. */ 812 *n_devs = 0; 813 814 fd = open(cfg->auth_file, O_RDONLY | O_CLOEXEC | O_NOCTTY); 815 if (fd < 0) { 816 if (cfg->debug) 817 D(cfg->debug_file, "Cannot open file: %s (%s)", cfg->auth_file, 818 strerror(errno)); 819 goto err; 820 } 821 822 if (fstat(fd, &st) < 0) { 823 if (cfg->debug) 824 D(cfg->debug_file, "Cannot stat file: %s (%s)", cfg->auth_file, 825 strerror(errno)); 826 goto err; 827 } 828 829 if (!S_ISREG(st.st_mode)) { 830 if (cfg->debug) 831 D(cfg->debug_file, "%s is not a regular file", cfg->auth_file); 832 goto err; 833 } 834 835 if (st.st_size == 0) { 836 if (cfg->debug) 837 D(cfg->debug_file, "File %s is empty", cfg->auth_file); 838 goto err; 839 } 840 opwfile_size = (size_t)st.st_size; 841 842 gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw); 843 if (gpu_ret != 0 || pw == NULL) { 844 D(cfg->debug_file, "Unable to retrieve credentials for uid %u, (%s)", 845 st.st_uid, strerror(errno)); 846 goto err; 847 } 848 849 if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) { 850 if (strcmp(username, "root") != 0) { 851 D(cfg->debug_file, 852 "The owner of the authentication file is neither %s nor root", 853 username); 854 } else { 855 D(cfg->debug_file, "The owner of the authentication file is not root"); 856 } 857 goto err; 858 } 859 860 opwfile = fdopen(fd, "r"); 861 if (opwfile == NULL) { 862 if (cfg->debug) 863 D(cfg->debug_file, "fdopen: %s", strerror(errno)); 864 goto err; 865 } else { 866 fd = -1; /* fd belongs to opwfile */ 867 } 868 869 buf = calloc(1, (DEVSIZE * cfg->max_devs)); 870 if (!buf) { 871 if (cfg->debug) 872 D(cfg->debug_file, "Unable to allocate memory"); 873 goto err; 874 } 875 876 if (cfg->sshformat == 0) { 877 retval = parse_native_format(cfg, username, buf, opwfile, devices, n_devs); 878 } else { 879 retval = parse_ssh_format(cfg, buf, DEVSIZE * cfg->max_devs, opwfile, 880 opwfile_size, devices, n_devs); 881 } 882 883 if (retval != 1) { 884 // NOTE(adma): error message is logged by the previous function 885 goto err; 886 } 887 888 if (cfg->debug) 889 D(cfg->debug_file, "Found %d device(s) for user %s", *n_devs, username); 890 891 retval = 1; 892 goto out; 893 894 err: 895 for (i = 0; i < *n_devs; i++) { 896 reset_device(&devices[i]); 897 } 898 899 *n_devs = 0; 900 901 out: 902 if (buf) { 903 free(buf); 904 buf = NULL; 905 } 906 907 if (opwfile) 908 fclose(opwfile); 909 910 if (fd != -1) 911 close(fd); 912 913 return retval; 914 } 915 916 void free_devices(device_t *devices, const unsigned n_devs) { 917 unsigned i; 918 919 if (!devices) 920 return; 921 922 for (i = 0; i < n_devs; i++) { 923 reset_device(&devices[i]); 924 } 925 926 free(devices); 927 devices = NULL; 928 } 929 930 static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist, 931 size_t devlist_len, fido_assert_t *assert, 932 const int rk, fido_dev_t **authlist) { 933 const fido_dev_info_t *di = NULL; 934 fido_dev_t *dev = NULL; 935 int r; 936 size_t i; 937 size_t j; 938 939 if (cfg->debug) 940 D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len); 941 942 for (i = 0, j = 0; i < devlist_len; i++) { 943 if (cfg->debug) 944 D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i); 945 946 di = fido_dev_info_ptr(devlist, i); 947 if (!di) { 948 if (cfg->debug) 949 D(cfg->debug_file, "Unable to get device pointer"); 950 continue; 951 } 952 953 if (cfg->debug) 954 D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di)); 955 956 dev = fido_dev_new(); 957 if (!dev) { 958 if (cfg->debug) 959 D(cfg->debug_file, "Unable to allocate device type"); 960 continue; 961 } 962 963 r = fido_dev_open(dev, fido_dev_info_path(di)); 964 if (r != FIDO_OK) { 965 if (cfg->debug) 966 D(cfg->debug_file, "Failed to open authenticator: %s (%d)", 967 fido_strerr(r), r); 968 fido_dev_free(&dev); 969 continue; 970 } 971 972 if (rk || cfg->nodetect) { 973 /* resident credential or nodetect: try all authenticators */ 974 authlist[j++] = dev; 975 } else { 976 r = fido_dev_get_assert(dev, assert, NULL); 977 if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) || 978 (fido_dev_is_fido2(dev) && r == FIDO_OK)) { 979 authlist[j++] = dev; 980 if (cfg->debug) 981 D(cfg->debug_file, "Found key in authenticator %zu", i); 982 return (1); 983 } 984 if (cfg->debug) 985 D(cfg->debug_file, "Key not found in authenticator %zu", i); 986 987 fido_dev_close(dev); 988 fido_dev_free(&dev); 989 } 990 } 991 992 if (j != 0) 993 return (1); 994 else { 995 if (cfg->debug) 996 D(cfg->debug_file, "Key not found"); 997 return (0); 998 } 999 } 1000 1001 static void init_opts(struct opts *opts) { 1002 opts->up = FIDO_OPT_FALSE; 1003 opts->uv = FIDO_OPT_OMIT; 1004 opts->pin = FIDO_OPT_FALSE; 1005 } 1006 1007 static void parse_opts(const cfg_t *cfg, const char *attr, struct opts *opts) { 1008 if (cfg->userpresence == 1 || strstr(attr, "+presence")) { 1009 opts->up = FIDO_OPT_TRUE; 1010 } else if (cfg->userpresence == 0) { 1011 opts->up = FIDO_OPT_FALSE; 1012 } else { 1013 opts->up = FIDO_OPT_OMIT; 1014 } 1015 1016 if (cfg->userverification == 1 || strstr(attr, "+verification")) { 1017 opts->uv = FIDO_OPT_TRUE; 1018 } else if (cfg->userverification == 0) 1019 opts->uv = FIDO_OPT_FALSE; 1020 else { 1021 opts->uv = FIDO_OPT_OMIT; 1022 } 1023 1024 if (cfg->pinverification == 1 || strstr(attr, "+pin")) { 1025 opts->pin = FIDO_OPT_TRUE; 1026 } else if (cfg->pinverification == 0) { 1027 opts->pin = FIDO_OPT_FALSE; 1028 } else { 1029 opts->pin = FIDO_OPT_OMIT; 1030 } 1031 } 1032 1033 static int get_device_opts(fido_dev_t *dev, int *pin, int *uv) { 1034 fido_cbor_info_t *info = NULL; 1035 char *const *ptr; 1036 const bool *val; 1037 size_t len; 1038 1039 *pin = *uv = -1; /* unsupported */ 1040 1041 if (fido_dev_is_fido2(dev)) { 1042 if ((info = fido_cbor_info_new()) == NULL || 1043 fido_dev_get_cbor_info(dev, info) != FIDO_OK) { 1044 fido_cbor_info_free(&info); 1045 return 0; 1046 } 1047 1048 ptr = fido_cbor_info_options_name_ptr(info); 1049 val = fido_cbor_info_options_value_ptr(info); 1050 len = fido_cbor_info_options_len(info); 1051 for (size_t i = 0; i < len; i++) { 1052 if (strcmp(ptr[i], "clientPin") == 0) { 1053 *pin = val[i]; 1054 } else if (strcmp(ptr[i], "uv") == 0) { 1055 *uv = val[i]; 1056 } 1057 } 1058 } 1059 1060 fido_cbor_info_free(&info); 1061 return 1; 1062 } 1063 1064 static int match_device_opts(fido_dev_t *dev, struct opts *opts) { 1065 int pin, uv; 1066 1067 /* FIXME: fido_dev_{supports,has}_{pin,uv} (1.7.0) */ 1068 if (!get_device_opts(dev, &pin, &uv)) { 1069 return -1; 1070 } 1071 1072 if (opts->uv == FIDO_OPT_FALSE && uv < 0) { 1073 opts->uv = FIDO_OPT_OMIT; 1074 } 1075 1076 if ((opts->pin == FIDO_OPT_TRUE && pin != 1) || 1077 (opts->uv == FIDO_OPT_TRUE && uv != 1)) { 1078 return 0; 1079 } 1080 1081 return 1; 1082 } 1083 1084 static int set_opts(const cfg_t *cfg, const struct opts *opts, 1085 fido_assert_t *assert) { 1086 if (fido_assert_set_up(assert, opts->up) != FIDO_OK) { 1087 if (cfg->debug) 1088 D(cfg->debug_file, "Failed to set UP"); 1089 return 0; 1090 } 1091 if (fido_assert_set_uv(assert, opts->uv) != FIDO_OK) { 1092 if (cfg->debug) 1093 D(cfg->debug_file, "Failed to set UV"); 1094 return 0; 1095 } 1096 1097 return 1; 1098 } 1099 1100 static int set_cdh(const cfg_t *cfg, fido_assert_t *assert) { 1101 unsigned char cdh[32]; 1102 int r; 1103 1104 if (!random_bytes(cdh, sizeof(cdh))) { 1105 if (cfg->debug) 1106 D(cfg->debug_file, "Failed to generate challenge"); 1107 return 0; 1108 } 1109 1110 r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh)); 1111 if (r != FIDO_OK) { 1112 if (cfg->debug) 1113 D(cfg->debug_file, "Unable to set challenge: %s (%d)", fido_strerr(r), r); 1114 return 0; 1115 } 1116 1117 return 1; 1118 } 1119 1120 static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, 1121 const struct opts *opts) { 1122 fido_assert_t *assert = NULL; 1123 unsigned char *buf = NULL; 1124 size_t buf_len; 1125 int ok = 0; 1126 int r; 1127 1128 if ((assert = fido_assert_new()) == NULL) { 1129 if (cfg->debug) 1130 D(cfg->debug_file, "Unable to allocate assertion"); 1131 goto err; 1132 } 1133 1134 if (device->old_format && strcmp(cfg->origin, cfg->appid) != 0) 1135 r = fido_assert_set_rp(assert, cfg->appid); 1136 else 1137 r = fido_assert_set_rp(assert, cfg->origin); 1138 1139 if (r != FIDO_OK) { 1140 if (cfg->debug) 1141 D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r); 1142 goto err; 1143 } 1144 1145 if (is_resident(device->keyHandle)) { 1146 if (cfg->debug) 1147 D(cfg->debug_file, "Credential is resident"); 1148 } else { 1149 if (cfg->debug) 1150 D(cfg->debug_file, "Key handle: %s", device->keyHandle); 1151 if (!b64_decode(device->keyHandle, (void **) &buf, &buf_len)) { 1152 if (cfg->debug) 1153 D(cfg->debug_file, "Failed to decode key handle"); 1154 goto err; 1155 } 1156 1157 r = fido_assert_allow_cred(assert, buf, buf_len); 1158 if (r != FIDO_OK) { 1159 if (cfg->debug) 1160 D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r), 1161 r); 1162 goto err; 1163 } 1164 } 1165 1166 if (!set_opts(cfg, opts, assert)) { 1167 if (cfg->debug) 1168 D(cfg->debug_file, "Failed to set assert options"); 1169 goto err; 1170 } 1171 1172 if (!set_cdh(cfg, assert)) { 1173 if (cfg->debug) 1174 D(cfg->debug_file, "Failed to set client data hash"); 1175 goto err; 1176 } 1177 1178 ok = 1; 1179 1180 err: 1181 if (!ok) 1182 fido_assert_free(&assert); 1183 1184 free(buf); 1185 1186 return assert; 1187 } 1188 1189 static void reset_pk(struct pk *pk) { 1190 if (pk->type == COSE_ES256) { 1191 es256_pk_free((es256_pk_t **) &pk->ptr); 1192 } else if (pk->type == COSE_RS256) { 1193 rs256_pk_free((rs256_pk_t **) &pk->ptr); 1194 } else if (pk->type == COSE_EDDSA) { 1195 eddsa_pk_free((eddsa_pk_t **) &pk->ptr); 1196 } 1197 memset(pk, 0, sizeof(*pk)); 1198 } 1199 1200 int cose_type(const char *str, int *type) { 1201 if (strcasecmp(str, "es256") == 0) { 1202 *type = COSE_ES256; 1203 } else if (strcasecmp(str, "rs256") == 0) { 1204 *type = COSE_RS256; 1205 } else if (strcasecmp(str, "eddsa") == 0) { 1206 *type = COSE_EDDSA; 1207 } else { 1208 *type = 0; 1209 return 0; 1210 } 1211 1212 return 1; 1213 } 1214 1215 const char *cose_string(int type) { 1216 switch (type) { 1217 case COSE_ES256: 1218 return "es256"; 1219 case COSE_RS256: 1220 return "rs256"; 1221 case COSE_EDDSA: 1222 return "eddsa"; 1223 default: 1224 return "unknown"; 1225 } 1226 } 1227 1228 static int parse_pk(const cfg_t *cfg, int old, const char *type, const char *pk, 1229 struct pk *out) { 1230 unsigned char *buf = NULL; 1231 size_t buf_len; 1232 int ok = 0; 1233 int r; 1234 1235 reset_pk(out); 1236 1237 if (old) { 1238 if (!hex_decode(pk, &buf, &buf_len)) { 1239 if (cfg->debug) 1240 D(cfg->debug_file, "Failed to decode public key"); 1241 goto err; 1242 } 1243 } else { 1244 if (!b64_decode(pk, (void **) &buf, &buf_len)) { 1245 if (cfg->debug) 1246 D(cfg->debug_file, "Failed to decode public key"); 1247 goto err; 1248 } 1249 } 1250 1251 if (!cose_type(type, &out->type)) { 1252 if (cfg->debug) 1253 D(cfg->debug_file, "Unknown COSE type '%s'", type); 1254 goto err; 1255 } 1256 1257 // For backwards compatibility, failure to pack the public key is not 1258 // returned as an error. Instead, it is handled by fido_verify_assert(). 1259 if (out->type == COSE_ES256) { 1260 if ((out->ptr = es256_pk_new()) == NULL) { 1261 if (cfg->debug) 1262 D(cfg->debug_file, "Failed to allocate ES256 public key"); 1263 goto err; 1264 } 1265 if (old) { 1266 r = translate_old_format_pubkey(out->ptr, buf, buf_len); 1267 } else { 1268 r = es256_pk_from_ptr(out->ptr, buf, buf_len); 1269 } 1270 if (r != FIDO_OK) { 1271 if (cfg->debug) 1272 D(cfg->debug_file, "Failed to convert ES256 public key"); 1273 } 1274 } else if (out->type == COSE_RS256) { 1275 if ((out->ptr = rs256_pk_new()) == NULL) { 1276 if (cfg->debug) 1277 D(cfg->debug_file, "Failed to allocate RS256 public key"); 1278 goto err; 1279 } 1280 r = rs256_pk_from_ptr(out->ptr, buf, buf_len); 1281 if (r != FIDO_OK) { 1282 if (cfg->debug) 1283 D(cfg->debug_file, "Failed to convert RS256 public key"); 1284 } 1285 } else if (out->type == COSE_EDDSA) { 1286 if ((out->ptr = eddsa_pk_new()) == NULL) { 1287 if (cfg->debug) 1288 D(cfg->debug_file, "Failed to allocate EDDSA public key"); 1289 goto err; 1290 } 1291 r = eddsa_pk_from_ptr(out->ptr, buf, buf_len); 1292 if (r != FIDO_OK) { 1293 if (cfg->debug) 1294 D(cfg->debug_file, "Failed to convert EDDSA public key"); 1295 } 1296 } else { 1297 if (cfg->debug) 1298 D(cfg->debug_file, "COSE type '%s' not handled", type); 1299 goto err; 1300 } 1301 1302 ok = 1; 1303 err: 1304 free(buf); 1305 1306 return ok; 1307 } 1308 1309 int do_authentication(const cfg_t *cfg, const device_t *devices, 1310 const unsigned n_devs, pam_handle_t *pamh) { 1311 fido_assert_t *assert = NULL; 1312 fido_dev_info_t *devlist = NULL; 1313 fido_dev_t **authlist = NULL; 1314 int cued = 0; 1315 int r; 1316 int retval = -2; 1317 size_t ndevs = 0; 1318 size_t ndevs_prev = 0; 1319 unsigned i = 0; 1320 struct opts opts; 1321 struct pk pk; 1322 char *pin = NULL; 1323 1324 init_opts(&opts); 1325 #ifndef WITH_FUZZING 1326 fido_init(cfg->debug ? FIDO_DEBUG : 0); 1327 #else 1328 fido_init(0); 1329 #endif 1330 memset(&pk, 0, sizeof(pk)); 1331 1332 devlist = fido_dev_info_new(64); 1333 if (!devlist) { 1334 if (cfg->debug) 1335 D(cfg->debug_file, "Unable to allocate devlist"); 1336 goto out; 1337 } 1338 1339 r = fido_dev_info_manifest(devlist, 64, &ndevs); 1340 if (r != FIDO_OK) { 1341 if (cfg->debug) 1342 D(cfg->debug_file, "Unable to discover device(s), %s (%d)", 1343 fido_strerr(r), r); 1344 goto out; 1345 } 1346 1347 ndevs_prev = ndevs; 1348 1349 if (cfg->debug) 1350 D(cfg->debug_file, "Device max index is %zu", ndevs); 1351 1352 authlist = calloc(64 + 1, sizeof(fido_dev_t *)); 1353 if (!authlist) { 1354 if (cfg->debug) 1355 D(cfg->debug_file, "Unable to allocate authenticator list"); 1356 goto out; 1357 } 1358 1359 if (cfg->nodetect && cfg->debug) 1360 D(cfg->debug_file, 1361 "nodetect option specified, suitable key detection will be skipped"); 1362 1363 i = 0; 1364 while (i < n_devs) { 1365 retval = -2; 1366 1367 if (cfg->debug) 1368 D(cfg->debug_file, "Attempting authentication with device number %d", 1369 i + 1); 1370 1371 init_opts(&opts); /* used during authenticator discovery */ 1372 assert = prepare_assert(cfg, &devices[i], &opts); 1373 if (assert == NULL) { 1374 if (cfg->debug) 1375 D(cfg->debug_file, "Failed to prepare assert"); 1376 goto out; 1377 } 1378 1379 if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType, 1380 devices[i].publicKey, &pk)) { 1381 if (cfg->debug) 1382 D(cfg->debug_file, "Failed to parse public key"); 1383 goto out; 1384 } 1385 1386 if (get_authenticators(cfg, devlist, ndevs, assert, 1387 is_resident(devices[i].keyHandle), authlist)) { 1388 for (size_t j = 0; authlist[j] != NULL; j++) { 1389 /* options used during authentication */ 1390 parse_opts(cfg, devices[i].attributes, &opts); 1391 1392 r = match_device_opts(authlist[j], &opts); 1393 if (r != 1) { 1394 if (cfg->debug) { 1395 D(cfg->debug_file, "%s, skipping authenticator", 1396 r < 0 ? "Failed to query supported options" 1397 : "Unsupported options"); 1398 } 1399 continue; 1400 } 1401 1402 if (!set_opts(cfg, &opts, assert)) { 1403 if (cfg->debug) 1404 D(cfg->debug_file, "Failed to set assert options"); 1405 goto out; 1406 } 1407 1408 if (!set_cdh(cfg, assert)) { 1409 if (cfg->debug) 1410 D(cfg->debug_file, "Failed to reset client data hash"); 1411 goto out; 1412 } 1413 1414 if (opts.pin == FIDO_OPT_TRUE) { 1415 pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: "); 1416 if (pin == NULL) { 1417 D(cfg->debug_file, "converse() returned NULL"); 1418 goto out; 1419 } 1420 } 1421 if (opts.up == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) { 1422 if (cfg->manual == 0 && cfg->cue && !cued) { 1423 cued = 1; 1424 converse(pamh, PAM_TEXT_INFO, 1425 cfg->cue_prompt != NULL ? cfg->cue_prompt : DEFAULT_CUE); 1426 } 1427 } 1428 r = fido_dev_get_assert(authlist[j], assert, pin); 1429 if (pin) { 1430 explicit_bzero(pin, strlen(pin)); 1431 free(pin); 1432 pin = NULL; 1433 } 1434 if (r == FIDO_OK) { 1435 if (opts.pin == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) { 1436 r = fido_assert_set_uv(assert, FIDO_OPT_TRUE); 1437 if (r != FIDO_OK) { 1438 D(cfg->debug_file, "Failed to set UV"); 1439 goto out; 1440 } 1441 } 1442 r = fido_assert_verify(assert, 0, pk.type, pk.ptr); 1443 if (r == FIDO_OK) { 1444 retval = 1; 1445 goto out; 1446 } 1447 } 1448 } 1449 } else { 1450 if (cfg->debug) 1451 D(cfg->debug_file, "Device for this keyhandle is not present"); 1452 } 1453 1454 i++; 1455 1456 fido_dev_info_free(&devlist, ndevs); 1457 1458 devlist = fido_dev_info_new(64); 1459 if (!devlist) { 1460 if (cfg->debug) 1461 D(cfg->debug_file, "Unable to allocate devlist"); 1462 goto out; 1463 } 1464 1465 r = fido_dev_info_manifest(devlist, 64, &ndevs); 1466 if (r != FIDO_OK) { 1467 if (cfg->debug) 1468 D(cfg->debug_file, "Unable to discover device(s), %s (%d)", 1469 fido_strerr(r), r); 1470 goto out; 1471 } 1472 1473 if (ndevs > ndevs_prev) { 1474 if (cfg->debug) 1475 D(cfg->debug_file, 1476 "Devices max_index has changed: %zu (was %zu). Starting over", ndevs, 1477 ndevs_prev); 1478 ndevs_prev = ndevs; 1479 i = 0; 1480 } 1481 1482 for (size_t j = 0; authlist[j] != NULL; j++) { 1483 fido_dev_close(authlist[j]); 1484 fido_dev_free(&authlist[j]); 1485 } 1486 1487 fido_assert_free(&assert); 1488 } 1489 1490 out: 1491 reset_pk(&pk); 1492 fido_assert_free(&assert); 1493 fido_dev_info_free(&devlist, ndevs); 1494 1495 if (authlist) { 1496 for (size_t j = 0; authlist[j] != NULL; j++) { 1497 fido_dev_close(authlist[j]); 1498 fido_dev_free(&authlist[j]); 1499 } 1500 free(authlist); 1501 } 1502 1503 return retval; 1504 } 1505 1506 #define MAX_PROMPT_LEN (1024) 1507 1508 static int manual_get_assert(const cfg_t *cfg, const char *prompt, 1509 pam_handle_t *pamh, fido_assert_t *assert) { 1510 char *b64_cdh = NULL; 1511 char *b64_rpid = NULL; 1512 char *b64_authdata = NULL; 1513 char *b64_sig = NULL; 1514 unsigned char *authdata = NULL; 1515 unsigned char *sig = NULL; 1516 size_t authdata_len; 1517 size_t sig_len; 1518 int r; 1519 int ok = 0; 1520 1521 b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); 1522 b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); 1523 b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); 1524 b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt); 1525 1526 if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) { 1527 if (cfg->debug) 1528 D(cfg->debug_file, "Failed to decode authenticator data"); 1529 goto err; 1530 } 1531 1532 if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) { 1533 if (cfg->debug) 1534 D(cfg->debug_file, "Failed to decode signature"); 1535 goto err; 1536 } 1537 1538 r = fido_assert_set_count(assert, 1); 1539 if (r != FIDO_OK) { 1540 if (cfg->debug) 1541 D(cfg->debug_file, "Failed to set signature count of assertion"); 1542 goto err; 1543 } 1544 1545 r = fido_assert_set_authdata(assert, 0, authdata, authdata_len); 1546 if (r != FIDO_OK) { 1547 if (cfg->debug) 1548 D(cfg->debug_file, "Failed to set authdata of assertion"); 1549 goto err; 1550 } 1551 1552 r = fido_assert_set_sig(assert, 0, sig, sig_len); 1553 if (r != FIDO_OK) { 1554 if (cfg->debug) 1555 D(cfg->debug_file, "Failed to set signature of assertion"); 1556 goto err; 1557 } 1558 1559 ok = 1; 1560 err: 1561 free(b64_cdh); 1562 free(b64_rpid); 1563 free(b64_authdata); 1564 free(b64_sig); 1565 free(authdata); 1566 free(sig); 1567 1568 return ok; 1569 } 1570 1571 int do_manual_authentication(const cfg_t *cfg, const device_t *devices, 1572 const unsigned n_devs, pam_handle_t *pamh) { 1573 fido_assert_t **assert = NULL; 1574 struct pk *pk = NULL; 1575 char *b64_challenge = NULL; 1576 char prompt[MAX_PROMPT_LEN]; 1577 char buf[MAX_PROMPT_LEN]; 1578 int retval = -2; 1579 int n; 1580 int r; 1581 unsigned i = 0; 1582 struct opts opts; 1583 1584 init_opts(&opts); 1585 assert = calloc(n_devs, sizeof(*assert)); 1586 if (assert == NULL) 1587 goto out; 1588 pk = calloc(n_devs, sizeof(*pk)); 1589 if (pk == NULL) 1590 goto out; 1591 1592 #ifndef WITH_FUZZING 1593 fido_init(cfg->debug ? FIDO_DEBUG : 0); 1594 #else 1595 fido_init(0); 1596 #endif 1597 1598 for (i = 0; i < n_devs; ++i) { 1599 /* options used during authentication */ 1600 parse_opts(cfg, devices[i].attributes, &opts); 1601 assert[i] = prepare_assert(cfg, &devices[i], &opts); 1602 if (assert[i] == NULL) { 1603 if (cfg->debug) 1604 D(cfg->debug_file, "Failed to prepare assert"); 1605 goto out; 1606 } 1607 1608 if (cfg->debug) 1609 D(cfg->debug_file, "Attempting authentication with device number %d", 1610 i + 1); 1611 1612 if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType, 1613 devices[i].publicKey, &pk[i])) { 1614 if (cfg->debug) 1615 D(cfg->debug_file, "Unable to parse public key %u", i); 1616 goto out; 1617 } 1618 1619 if (!b64_encode(fido_assert_clientdata_hash_ptr(assert[i]), 1620 fido_assert_clientdata_hash_len(assert[i]), 1621 &b64_challenge)) { 1622 if (cfg->debug) 1623 D(cfg->debug_file, "Failed to encode challenge"); 1624 goto out; 1625 } 1626 1627 if (cfg->debug) 1628 D(cfg->debug_file, "Challenge: %s", b64_challenge); 1629 1630 n = snprintf(prompt, sizeof(prompt), "Challenge #%d:", i + 1); 1631 if (n <= 0 || (size_t) n >= sizeof(prompt)) { 1632 if (cfg->debug) 1633 D(cfg->debug_file, "Failed to print challenge prompt"); 1634 goto out; 1635 } 1636 1637 converse(pamh, PAM_TEXT_INFO, prompt); 1638 1639 n = snprintf(buf, sizeof(buf), "%s\n%s\n%s", b64_challenge, cfg->origin, 1640 devices[i].keyHandle); 1641 if (n <= 0 || (size_t) n >= sizeof(buf)) { 1642 if (cfg->debug) 1643 D(cfg->debug_file, "Failed to print fido2-assert input string"); 1644 goto out; 1645 } 1646 1647 converse(pamh, PAM_TEXT_INFO, buf); 1648 1649 free(b64_challenge); 1650 b64_challenge = NULL; 1651 } 1652 1653 converse(pamh, PAM_TEXT_INFO, 1654 "Please pass the challenge(s) above to fido2-assert, and " 1655 "paste the results in the prompt below."); 1656 1657 retval = -1; 1658 1659 for (i = 0; i < n_devs; ++i) { 1660 n = snprintf(prompt, sizeof(prompt), "Response #%d: ", i + 1); 1661 if (n <= 0 || (size_t) n >= sizeof(prompt)) { 1662 if (cfg->debug) 1663 D(cfg->debug_file, "Failed to print response prompt"); 1664 goto out; 1665 } 1666 1667 if (!manual_get_assert(cfg, prompt, pamh, assert[i])) { 1668 if (cfg->debug) 1669 D(cfg->debug_file, "Failed to get assert %u", i); 1670 goto out; 1671 } 1672 1673 r = fido_assert_verify(assert[i], 0, pk[i].type, pk[i].ptr); 1674 if (r == FIDO_OK) { 1675 retval = 1; 1676 break; 1677 } 1678 } 1679 1680 out: 1681 for (i = 0; i < n_devs; i++) { 1682 fido_assert_free(&assert[i]); 1683 reset_pk(&pk[i]); 1684 } 1685 free(assert); 1686 free(pk); 1687 free(b64_challenge); 1688 1689 return retval; 1690 } 1691 1692 static int _converse(pam_handle_t *pamh, int nargs, 1693 const struct pam_message **message, 1694 struct pam_response **response) { 1695 struct pam_conv *conv; 1696 int retval; 1697 1698 retval = pam_get_item(pamh, PAM_CONV, (void *) &conv); 1699 1700 if (retval != PAM_SUCCESS) { 1701 return retval; 1702 } 1703 1704 return conv->conv(nargs, message, response, conv->appdata_ptr); 1705 } 1706 1707 char *converse(pam_handle_t *pamh, int echocode, const char *prompt) { 1708 const struct pam_message msg = {.msg_style = echocode, 1709 .msg = (char *) (uintptr_t) prompt}; 1710 const struct pam_message *msgs = &msg; 1711 struct pam_response *resp = NULL; 1712 int retval = _converse(pamh, 1, &msgs, &resp); 1713 char *ret = NULL; 1714 1715 if (retval != PAM_SUCCESS || resp == NULL || resp->resp == NULL || 1716 *resp->resp == '\000') { 1717 1718 if (retval == PAM_SUCCESS && resp && resp->resp) { 1719 ret = resp->resp; 1720 } 1721 } else { 1722 ret = resp->resp; 1723 } 1724 1725 // Deallocate temporary storage. 1726 if (resp) { 1727 if (!ret) { 1728 free(resp->resp); 1729 } 1730 free(resp); 1731 } 1732 1733 return ret; 1734 } 1735 1736 #if defined(PAM_DEBUG) 1737 void _debug(FILE *debug_file, const char *file, int line, const char *func, 1738 const char *fmt, ...) { 1739 va_list ap; 1740 va_start(ap, fmt); 1741 1742 #if defined(WITH_FUZZING) 1743 (void) debug_file; 1744 snprintf(NULL, 0, DEBUG_STR, file, line, func); 1745 vsnprintf(NULL, 0, fmt, ap); 1746 #elif defined(LOG_DEBUG) 1747 if (debug_file == (FILE *) -1) { 1748 syslog(LOG_AUTHPRIV | LOG_DEBUG, DEBUG_STR, file, line, func); 1749 vsyslog(LOG_AUTHPRIV | LOG_DEBUG, fmt, ap); 1750 } else { 1751 fprintf(debug_file, DEBUG_STR, file, line, func); 1752 vfprintf(debug_file, fmt, ap); 1753 fprintf(debug_file, "\n"); 1754 } 1755 #else /* Windows, MAC */ 1756 fprintf(debug_file, DEBUG_STR, file, line, func); 1757 vfprintf(debug_file, fmt, ap); 1758 fprintf(debug_file, "\n"); 1759 #endif /* __linux__ */ 1760 va_end(ap); 1761 } 1762 #endif /* PAM_DEBUG */ 1763 1764 #ifndef RANDOM_DEV 1765 #define RANDOM_DEV "/dev/urandom" 1766 #endif 1767 1768 int random_bytes(void *buf, size_t cnt) { 1769 int fd; 1770 ssize_t n; 1771 1772 fd = open(RANDOM_DEV, O_RDONLY); 1773 if (fd < 0) 1774 return (0); 1775 1776 n = read(fd, buf, cnt); 1777 close(fd); 1778 if (n < 0 || (size_t) n != cnt) 1779 return (0); 1780 1781 return (1); 1782 } 1783