1/* $OpenBSD: auth2-pubkey.c,v 1.47 2015/02/17 00:14:05 djm Exp $ */ 2/* 3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "includes.h" 27 28#include <sys/types.h> 29#include <sys/stat.h> 30#include <sys/wait.h> 31 32#include <errno.h> 33#include <fcntl.h> 34#ifdef HAVE_PATHS_H 35# include <paths.h> 36#endif 37#include <pwd.h> 38#include <signal.h> 39#include <stdio.h> 40#include <stdarg.h> 41#include <string.h> 42#include <time.h> 43#include <unistd.h> 44#include <limits.h> 45 46#include "xmalloc.h" 47#include "ssh.h" 48#include "ssh2.h" 49#include "packet.h" 50#include "buffer.h" 51#include "log.h" 52#include "misc.h" 53#include "servconf.h" 54#include "compat.h" 55#include "key.h" 56#include "hostfile.h" 57#include "auth.h" 58#include "pathnames.h" 59#include "uidswap.h" 60#include "auth-options.h" 61#include "canohost.h" 62#ifdef GSSAPI 63#include "ssh-gss.h" 64#endif 65#include "monitor_wrap.h" 66#include "authfile.h" 67#include "match.h" 68 69/* import */ 70extern ServerOptions options; 71extern u_char *session_id2; 72extern u_int session_id2_len; 73 74static int 75userauth_pubkey(Authctxt *authctxt) 76{ 77 Buffer b; 78 Key *key = NULL; 79 char *pkalg, *userstyle; 80 u_char *pkblob, *sig; 81 u_int alen, blen, slen; 82 int have_sig, pktype; 83 int authenticated = 0; 84 85 if (!authctxt->valid) { 86 debug2("userauth_pubkey: disabled because of invalid user"); 87 return 0; 88 } 89 have_sig = packet_get_char(); 90 if (datafellows & SSH_BUG_PKAUTH) { 91 debug2("userauth_pubkey: SSH_BUG_PKAUTH"); 92 /* no explicit pkalg given */ 93 pkblob = packet_get_string(&blen); 94 buffer_init(&b); 95 buffer_append(&b, pkblob, blen); 96 /* so we have to extract the pkalg from the pkblob */ 97 pkalg = buffer_get_string(&b, &alen); 98 buffer_free(&b); 99 } else { 100 pkalg = packet_get_string(&alen); 101 pkblob = packet_get_string(&blen); 102 } 103 pktype = key_type_from_name(pkalg); 104 if (pktype == KEY_UNSPEC) { 105 /* this is perfectly legal */ 106 logit("userauth_pubkey: unsupported public key algorithm: %s", 107 pkalg); 108 goto done; 109 } 110 key = key_from_blob(pkblob, blen); 111 if (key == NULL) { 112 error("userauth_pubkey: cannot decode key: %s", pkalg); 113 goto done; 114 } 115 if (key->type != pktype) { 116 error("userauth_pubkey: type mismatch for decoded key " 117 "(received %d, expected %d)", key->type, pktype); 118 goto done; 119 } 120 if (key_type_plain(key->type) == KEY_RSA && 121 (datafellows & SSH_BUG_RSASIGMD5) != 0) { 122 logit("Refusing RSA key because client uses unsafe " 123 "signature scheme"); 124 goto done; 125 } 126 if (auth2_userkey_already_used(authctxt, key)) { 127 logit("refusing previously-used %s key", key_type(key)); 128 goto done; 129 } 130 if (match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 131 strlen(options.pubkey_key_types), 0) != 1) { 132 logit("%s: key type %s not in PubkeyAcceptedKeyTypes", 133 __func__, sshkey_ssh_name(key)); 134 goto done; 135 } 136 137 if (have_sig) { 138 sig = packet_get_string(&slen); 139 packet_check_eom(); 140 buffer_init(&b); 141 if (datafellows & SSH_OLD_SESSIONID) { 142 buffer_append(&b, session_id2, session_id2_len); 143 } else { 144 buffer_put_string(&b, session_id2, session_id2_len); 145 } 146 /* reconstruct packet */ 147 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 148 xasprintf(&userstyle, "%s%s%s", authctxt->user, 149 authctxt->style ? ":" : "", 150 authctxt->style ? authctxt->style : ""); 151 buffer_put_cstring(&b, userstyle); 152 free(userstyle); 153 buffer_put_cstring(&b, 154 datafellows & SSH_BUG_PKSERVICE ? 155 "ssh-userauth" : 156 authctxt->service); 157 if (datafellows & SSH_BUG_PKAUTH) { 158 buffer_put_char(&b, have_sig); 159 } else { 160 buffer_put_cstring(&b, "publickey"); 161 buffer_put_char(&b, have_sig); 162 buffer_put_cstring(&b, pkalg); 163 } 164 buffer_put_string(&b, pkblob, blen); 165#ifdef DEBUG_PK 166 buffer_dump(&b); 167#endif 168 pubkey_auth_info(authctxt, key, NULL); 169 170 /* test for correct signature */ 171 authenticated = 0; 172 if (PRIVSEP(user_key_allowed(authctxt->pw, key)) && 173 PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), 174 buffer_len(&b))) == 1) { 175 authenticated = 1; 176 /* Record the successful key to prevent reuse */ 177 auth2_record_userkey(authctxt, key); 178 key = NULL; /* Don't free below */ 179 } 180 buffer_free(&b); 181 free(sig); 182 } else { 183 debug("test whether pkalg/pkblob are acceptable"); 184 packet_check_eom(); 185 186 /* XXX fake reply and always send PK_OK ? */ 187 /* 188 * XXX this allows testing whether a user is allowed 189 * to login: if you happen to have a valid pubkey this 190 * message is sent. the message is NEVER sent at all 191 * if a user is not allowed to login. is this an 192 * issue? -markus 193 */ 194 if (PRIVSEP(user_key_allowed(authctxt->pw, key))) { 195 packet_start(SSH2_MSG_USERAUTH_PK_OK); 196 packet_put_string(pkalg, alen); 197 packet_put_string(pkblob, blen); 198 packet_send(); 199 packet_write_wait(); 200 authctxt->postponed = 1; 201 } 202 } 203 if (authenticated != 1) 204 auth_clear_options(); 205done: 206 debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); 207 if (key != NULL) 208 key_free(key); 209 free(pkalg); 210 free(pkblob); 211 return authenticated; 212} 213 214void 215pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) 216{ 217 char *fp, *extra; 218 va_list ap; 219 int i; 220 221 extra = NULL; 222 if (fmt != NULL) { 223 va_start(ap, fmt); 224 i = vasprintf(&extra, fmt, ap); 225 va_end(ap); 226 if (i < 0 || extra == NULL) 227 fatal("%s: vasprintf failed", __func__); 228 } 229 230 if (key_is_cert(key)) { 231 fp = sshkey_fingerprint(key->cert->signature_key, 232 options.fingerprint_hash, SSH_FP_DEFAULT); 233 auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", 234 key_type(key), key->cert->key_id, 235 (unsigned long long)key->cert->serial, 236 key_type(key->cert->signature_key), 237 fp == NULL ? "(null)" : fp, 238 extra == NULL ? "" : ", ", extra == NULL ? "" : extra); 239 free(fp); 240 } else { 241 fp = sshkey_fingerprint(key, options.fingerprint_hash, 242 SSH_FP_DEFAULT); 243 auth_info(authctxt, "%s %s%s%s", key_type(key), 244 fp == NULL ? "(null)" : fp, 245 extra == NULL ? "" : ", ", extra == NULL ? "" : extra); 246 free(fp); 247 } 248 free(extra); 249} 250 251static int 252match_principals_option(const char *principal_list, struct sshkey_cert *cert) 253{ 254 char *result; 255 u_int i; 256 257 /* XXX percent_expand() sequences for authorized_principals? */ 258 259 for (i = 0; i < cert->nprincipals; i++) { 260 if ((result = match_list(cert->principals[i], 261 principal_list, NULL)) != NULL) { 262 debug3("matched principal from key options \"%.100s\"", 263 result); 264 free(result); 265 return 1; 266 } 267 } 268 return 0; 269} 270 271static int 272match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) 273{ 274 FILE *f; 275 char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; 276 u_long linenum = 0; 277 u_int i; 278 279 temporarily_use_uid(pw); 280 debug("trying authorized principals file %s", file); 281 if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { 282 restore_uid(); 283 return 0; 284 } 285 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 286 /* Skip leading whitespace. */ 287 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 288 ; 289 /* Skip blank and comment lines. */ 290 if ((ep = strchr(cp, '#')) != NULL) 291 *ep = '\0'; 292 if (!*cp || *cp == '\n') 293 continue; 294 /* Trim trailing whitespace. */ 295 ep = cp + strlen(cp) - 1; 296 while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) 297 *ep-- = '\0'; 298 /* 299 * If the line has internal whitespace then assume it has 300 * key options. 301 */ 302 line_opts = NULL; 303 if ((ep = strrchr(cp, ' ')) != NULL || 304 (ep = strrchr(cp, '\t')) != NULL) { 305 for (; *ep == ' ' || *ep == '\t'; ep++) 306 ; 307 line_opts = cp; 308 cp = ep; 309 } 310 for (i = 0; i < cert->nprincipals; i++) { 311 if (strcmp(cp, cert->principals[i]) == 0) { 312 debug3("matched principal \"%.100s\" " 313 "from file \"%s\" on line %lu", 314 cert->principals[i], file, linenum); 315 if (auth_parse_options(pw, line_opts, 316 file, linenum) != 1) 317 continue; 318 fclose(f); 319 restore_uid(); 320 return 1; 321 } 322 } 323 } 324 fclose(f); 325 restore_uid(); 326 return 0; 327} 328 329/* 330 * Checks whether key is allowed in authorized_keys-format file, 331 * returns 1 if the key is allowed or 0 otherwise. 332 */ 333static int 334check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) 335{ 336 char line[SSH_MAX_PUBKEY_BYTES]; 337 const char *reason; 338 int found_key = 0; 339 u_long linenum = 0; 340 Key *found; 341 char *fp; 342 343 found_key = 0; 344 345 found = NULL; 346 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 347 char *cp, *key_options = NULL; 348 if (found != NULL) 349 key_free(found); 350 found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); 351 auth_clear_options(); 352 353 /* Skip leading whitespace, empty and comment lines. */ 354 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 355 ; 356 if (!*cp || *cp == '\n' || *cp == '#') 357 continue; 358 359 if (key_read(found, &cp) != 1) { 360 /* no key? check if there are options for this key */ 361 int quoted = 0; 362 debug2("user_key_allowed: check options: '%s'", cp); 363 key_options = cp; 364 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 365 if (*cp == '\\' && cp[1] == '"') 366 cp++; /* Skip both */ 367 else if (*cp == '"') 368 quoted = !quoted; 369 } 370 /* Skip remaining whitespace. */ 371 for (; *cp == ' ' || *cp == '\t'; cp++) 372 ; 373 if (key_read(found, &cp) != 1) { 374 debug2("user_key_allowed: advance: '%s'", cp); 375 /* still no key? advance to next line*/ 376 continue; 377 } 378 } 379 if (key_is_cert(key)) { 380 if (!key_equal(found, key->cert->signature_key)) 381 continue; 382 if (auth_parse_options(pw, key_options, file, 383 linenum) != 1) 384 continue; 385 if (!key_is_cert_authority) 386 continue; 387 if ((fp = sshkey_fingerprint(found, 388 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 389 continue; 390 debug("matching CA found: file %s, line %lu, %s %s", 391 file, linenum, key_type(found), fp); 392 /* 393 * If the user has specified a list of principals as 394 * a key option, then prefer that list to matching 395 * their username in the certificate principals list. 396 */ 397 if (authorized_principals != NULL && 398 !match_principals_option(authorized_principals, 399 key->cert)) { 400 reason = "Certificate does not contain an " 401 "authorized principal"; 402 fail_reason: 403 free(fp); 404 error("%s", reason); 405 auth_debug_add("%s", reason); 406 continue; 407 } 408 if (key_cert_check_authority(key, 0, 0, 409 authorized_principals == NULL ? pw->pw_name : NULL, 410 &reason) != 0) 411 goto fail_reason; 412 if (auth_cert_options(key, pw) != 0) { 413 free(fp); 414 continue; 415 } 416 verbose("Accepted certificate ID \"%s\" " 417 "signed by %s CA %s via %s", key->cert->key_id, 418 key_type(found), fp, file); 419 free(fp); 420 found_key = 1; 421 break; 422 } else if (key_equal(found, key)) { 423 if (auth_parse_options(pw, key_options, file, 424 linenum) != 1) 425 continue; 426 if (key_is_cert_authority) 427 continue; 428 if ((fp = sshkey_fingerprint(found, 429 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 430 continue; 431 debug("matching key found: file %s, line %lu %s %s", 432 file, linenum, key_type(found), fp); 433 free(fp); 434 found_key = 1; 435 break; 436 } 437 } 438 if (found != NULL) 439 key_free(found); 440 if (!found_key) 441 debug2("key not found"); 442 return found_key; 443} 444 445/* Authenticate a certificate key against TrustedUserCAKeys */ 446static int 447user_cert_trusted_ca(struct passwd *pw, Key *key) 448{ 449 char *ca_fp, *principals_file = NULL; 450 const char *reason; 451 int ret = 0; 452 453 if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) 454 return 0; 455 456 if ((ca_fp = sshkey_fingerprint(key->cert->signature_key, 457 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 458 return 0; 459 460 if (sshkey_in_file(key->cert->signature_key, 461 options.trusted_user_ca_keys, 1, 0) != 0) { 462 debug2("%s: CA %s %s is not listed in %s", __func__, 463 key_type(key->cert->signature_key), ca_fp, 464 options.trusted_user_ca_keys); 465 goto out; 466 } 467 /* 468 * If AuthorizedPrincipals is in use, then compare the certificate 469 * principals against the names in that file rather than matching 470 * against the username. 471 */ 472 if ((principals_file = authorized_principals_file(pw)) != NULL) { 473 if (!match_principals_file(principals_file, pw, key->cert)) { 474 reason = "Certificate does not contain an " 475 "authorized principal"; 476 fail_reason: 477 error("%s", reason); 478 auth_debug_add("%s", reason); 479 goto out; 480 } 481 } 482 if (key_cert_check_authority(key, 0, 1, 483 principals_file == NULL ? pw->pw_name : NULL, &reason) != 0) 484 goto fail_reason; 485 if (auth_cert_options(key, pw) != 0) 486 goto out; 487 488 verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s", 489 key->cert->key_id, key_type(key->cert->signature_key), ca_fp, 490 options.trusted_user_ca_keys); 491 ret = 1; 492 493 out: 494 free(principals_file); 495 free(ca_fp); 496 return ret; 497} 498 499/* 500 * Checks whether key is allowed in file. 501 * returns 1 if the key is allowed or 0 otherwise. 502 */ 503static int 504user_key_allowed2(struct passwd *pw, Key *key, char *file) 505{ 506 FILE *f; 507 int found_key = 0; 508 509 /* Temporarily use the user's uid. */ 510 temporarily_use_uid(pw); 511 512 debug("trying public key file %s", file); 513 if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { 514 found_key = check_authkeys_file(f, file, key, pw); 515 fclose(f); 516 } 517 518 restore_uid(); 519 return found_key; 520} 521 522/* 523 * Checks whether key is allowed in output of command. 524 * returns 1 if the key is allowed or 0 otherwise. 525 */ 526static int 527user_key_command_allowed2(struct passwd *user_pw, Key *key) 528{ 529 FILE *f; 530 int ok, found_key = 0; 531 struct passwd *pw; 532 struct stat st; 533 int status, devnull, p[2], i; 534 pid_t pid; 535 char *username, errmsg[512]; 536 537 if (options.authorized_keys_command == NULL || 538 options.authorized_keys_command[0] != '/') 539 return 0; 540 541 if (options.authorized_keys_command_user == NULL) { 542 error("No user for AuthorizedKeysCommand specified, skipping"); 543 return 0; 544 } 545 546 username = percent_expand(options.authorized_keys_command_user, 547 "u", user_pw->pw_name, (char *)NULL); 548 pw = getpwnam(username); 549 if (pw == NULL) { 550 error("AuthorizedKeysCommandUser \"%s\" not found: %s", 551 username, strerror(errno)); 552 free(username); 553 return 0; 554 } 555 free(username); 556 557 temporarily_use_uid(pw); 558 559 if (stat(options.authorized_keys_command, &st) < 0) { 560 error("Could not stat AuthorizedKeysCommand \"%s\": %s", 561 options.authorized_keys_command, strerror(errno)); 562 goto out; 563 } 564 if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0, 565 errmsg, sizeof(errmsg)) != 0) { 566 error("Unsafe AuthorizedKeysCommand: %s", errmsg); 567 goto out; 568 } 569 570 if (pipe(p) != 0) { 571 error("%s: pipe: %s", __func__, strerror(errno)); 572 goto out; 573 } 574 575 debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"", 576 options.authorized_keys_command, user_pw->pw_name, pw->pw_name); 577 578 /* 579 * Don't want to call this in the child, where it can fatal() and 580 * run cleanup_exit() code. 581 */ 582 restore_uid(); 583 584 switch ((pid = fork())) { 585 case -1: /* error */ 586 error("%s: fork: %s", __func__, strerror(errno)); 587 close(p[0]); 588 close(p[1]); 589 return 0; 590 case 0: /* child */ 591 for (i = 0; i < NSIG; i++) 592 signal(i, SIG_DFL); 593 594 if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { 595 error("%s: open %s: %s", __func__, _PATH_DEVNULL, 596 strerror(errno)); 597 _exit(1); 598 } 599 /* Keep stderr around a while longer to catch errors */ 600 if (dup2(devnull, STDIN_FILENO) == -1 || 601 dup2(p[1], STDOUT_FILENO) == -1) { 602 error("%s: dup2: %s", __func__, strerror(errno)); 603 _exit(1); 604 } 605 closefrom(STDERR_FILENO + 1); 606 607 /* Don't use permanently_set_uid() here to avoid fatal() */ 608 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { 609 error("setresgid %u: %s", (u_int)pw->pw_gid, 610 strerror(errno)); 611 _exit(1); 612 } 613 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { 614 error("setresuid %u: %s", (u_int)pw->pw_uid, 615 strerror(errno)); 616 _exit(1); 617 } 618 /* stdin is pointed to /dev/null at this point */ 619 if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) { 620 error("%s: dup2: %s", __func__, strerror(errno)); 621 _exit(1); 622 } 623 624 execl(options.authorized_keys_command, 625 options.authorized_keys_command, user_pw->pw_name, NULL); 626 627 error("AuthorizedKeysCommand %s exec failed: %s", 628 options.authorized_keys_command, strerror(errno)); 629 _exit(127); 630 default: /* parent */ 631 break; 632 } 633 634 temporarily_use_uid(pw); 635 636 close(p[1]); 637 if ((f = fdopen(p[0], "r")) == NULL) { 638 error("%s: fdopen: %s", __func__, strerror(errno)); 639 close(p[0]); 640 /* Don't leave zombie child */ 641 kill(pid, SIGTERM); 642 while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) 643 ; 644 goto out; 645 } 646 ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); 647 fclose(f); 648 649 while (waitpid(pid, &status, 0) == -1) { 650 if (errno != EINTR) { 651 error("%s: waitpid: %s", __func__, strerror(errno)); 652 goto out; 653 } 654 } 655 if (WIFSIGNALED(status)) { 656 error("AuthorizedKeysCommand %s exited on signal %d", 657 options.authorized_keys_command, WTERMSIG(status)); 658 goto out; 659 } else if (WEXITSTATUS(status) != 0) { 660 error("AuthorizedKeysCommand %s returned status %d", 661 options.authorized_keys_command, WEXITSTATUS(status)); 662 goto out; 663 } 664 found_key = ok; 665 out: 666 restore_uid(); 667 return found_key; 668} 669 670/* 671 * Check whether key authenticates and authorises the user. 672 */ 673int 674user_key_allowed(struct passwd *pw, Key *key) 675{ 676 u_int success, i; 677 char *file; 678 679 if (auth_key_is_revoked(key)) 680 return 0; 681 if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) 682 return 0; 683 684 success = user_cert_trusted_ca(pw, key); 685 if (success) 686 return success; 687 688 success = user_key_command_allowed2(pw, key); 689 if (success > 0) 690 return success; 691 692 for (i = 0; !success && i < options.num_authkeys_files; i++) { 693 694 if (strcasecmp(options.authorized_keys_files[i], "none") == 0) 695 continue; 696 file = expand_authorized_keys( 697 options.authorized_keys_files[i], pw); 698 699 success = user_key_allowed2(pw, key, file); 700 free(file); 701 } 702 703 return success; 704} 705 706/* Records a public key in the list of previously-successful keys */ 707void 708auth2_record_userkey(Authctxt *authctxt, struct sshkey *key) 709{ 710 struct sshkey **tmp; 711 712 if (authctxt->nprev_userkeys >= INT_MAX || 713 (tmp = reallocarray(authctxt->prev_userkeys, 714 authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL) 715 fatal("%s: reallocarray failed", __func__); 716 authctxt->prev_userkeys = tmp; 717 authctxt->prev_userkeys[authctxt->nprev_userkeys] = key; 718 authctxt->nprev_userkeys++; 719} 720 721/* Checks whether a key has already been used successfully for authentication */ 722int 723auth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key) 724{ 725 u_int i; 726 727 for (i = 0; i < authctxt->nprev_userkeys; i++) { 728 if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) { 729 return 1; 730 } 731 } 732 return 0; 733} 734 735Authmethod method_pubkey = { 736 "publickey", 737 userauth_pubkey, 738 &options.pubkey_authentication 739}; 740