1 /* $NetBSD: auth2-pubkey.c,v 1.38 2026/04/08 18:58:40 christos Exp $ */ 2 /* $OpenBSD: auth2-pubkey.c,v 1.126 2026/04/02 07:48:13 djm Exp $ */ 3 4 /* 5 * Copyright (c) 2000 Markus Friedl. All rights reserved. 6 * Copyright (c) 2010 Damien Miller. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "includes.h" 30 __RCSID("$NetBSD: auth2-pubkey.c,v 1.38 2026/04/08 18:58:40 christos Exp $"); 31 #include <sys/types.h> 32 33 #include <stdlib.h> 34 #include <errno.h> 35 #include <paths.h> 36 #include <pwd.h> 37 #include <glob.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 "kex.h" 51 #include "sshbuf.h" 52 #include "log.h" 53 #include "misc.h" 54 #include "servconf.h" 55 #include "compat.h" 56 #include "sshkey.h" 57 #include "hostfile.h" 58 #include "auth.h" 59 #include "pathnames.h" 60 #include "uidswap.h" 61 #include "auth-options.h" 62 #include "canohost.h" 63 #ifdef GSSAPI 64 #include "ssh-gss.h" 65 #endif 66 #include "monitor_wrap.h" 67 #include "authfile.h" 68 #include "match.h" 69 #include "digest.h" 70 71 #ifdef WITH_LDAP_PUBKEY 72 #include "ldapauth.h" 73 #endif 74 75 #include "ssherr.h" 76 #include "channels.h" /* XXX for session.h */ 77 #include "session.h" /* XXX for child_set_env(); refactor? */ 78 #include "sk-api.h" 79 80 /* import */ 81 extern ServerOptions options; 82 extern struct authmethod_cfg methodcfg_pubkey; 83 84 static char * 85 format_key(const struct sshkey *key) 86 { 87 char *ret, *fp = sshkey_fingerprint(key, 88 options.fingerprint_hash, SSH_FP_DEFAULT); 89 90 xasprintf(&ret, "%s %s", sshkey_type(key), fp); 91 free(fp); 92 return ret; 93 } 94 95 static int 96 userauth_pubkey(struct ssh *ssh, const char *method) 97 { 98 Authctxt *authctxt = ssh->authctxt; 99 struct passwd *pw = authctxt->pw; 100 struct sshbuf *b = NULL; 101 struct sshkey *key = NULL, *hostkey = NULL; 102 char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; 103 u_char *pkblob = NULL, *sig = NULL, have_sig; 104 size_t blen, slen; 105 int hostbound, r, pktype; 106 int req_presence = 0, req_verify = 0, authenticated = 0; 107 struct sshauthopt *authopts = NULL; 108 struct sshkey_sig_details *sig_details = NULL; 109 110 hostbound = strcmp(method, "publickey-hostbound-v00 (at) openssh.com") == 0; 111 112 if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 || 113 (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || 114 (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) 115 fatal_fr(r, "parse %s packet", method); 116 117 /* hostbound auth includes the hostkey offered at initial KEX */ 118 if (hostbound) { 119 if ((r = sshpkt_getb_froms(ssh, &b)) != 0 || 120 (r = sshkey_fromb(b, &hostkey)) != 0) 121 fatal_fr(r, "parse %s hostkey", method); 122 if (ssh->kex->initial_hostkey == NULL) 123 fatal_f("internal error: initial hostkey not recorded"); 124 if (!sshkey_equal(hostkey, ssh->kex->initial_hostkey)) 125 fatal_f("%s packet contained wrong host key", method); 126 sshbuf_free(b); 127 b = NULL; 128 } 129 130 if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) { 131 char *keystring; 132 struct sshbuf *pkbuf; 133 134 if ((pkbuf = sshbuf_from(pkblob, blen)) == NULL) 135 fatal_f("sshbuf_from failed"); 136 if ((keystring = sshbuf_dtob64_string(pkbuf, 0)) == NULL) 137 fatal_f("sshbuf_dtob64 failed"); 138 debug2_f("%s user %s %s public key %s %s", 139 authctxt->valid ? "valid" : "invalid", authctxt->user, 140 have_sig ? "attempting" : "querying", pkalg, keystring); 141 sshbuf_free(pkbuf); 142 free(keystring); 143 } 144 145 pktype = sshkey_type_from_name(pkalg); 146 if (pktype == KEY_UNSPEC) { 147 /* this is perfectly legal */ 148 verbose_f("unsupported public key algorithm: %s", pkalg); 149 goto done; 150 } 151 if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { 152 error_fr(r, "parse key"); 153 goto done; 154 } 155 if (key == NULL) { 156 error_f("cannot decode key: %s", pkalg); 157 goto done; 158 } 159 if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA && 160 sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) { 161 error_f("key type mismatch for decoded key " 162 "(received %s, expected %s)", sshkey_ssh_name(key), pkalg); 163 goto done; 164 } 165 if (auth2_key_already_used(authctxt, key)) { 166 logit("refusing previously-used %s key", sshkey_type(key)); 167 goto done; 168 } 169 if (match_pattern_list(pkalg, options.pubkey_accepted_algos, 0) != 1) { 170 logit_f("signature algorithm %s not in " 171 "PubkeyAcceptedAlgorithms", pkalg); 172 goto done; 173 } 174 if ((r = sshkey_check_cert_sigtype(key, 175 options.ca_sign_algorithms)) != 0) { 176 logit_fr(r, "certificate signature algorithm %s", 177 (key->cert == NULL || key->cert->signature_type == NULL) ? 178 "(null)" : key->cert->signature_type); 179 goto done; 180 } 181 if ((r = sshkey_check_rsa_length(key, 182 options.required_rsa_size)) != 0) { 183 logit_r(r, "refusing %s key", sshkey_type(key)); 184 goto done; 185 } 186 key_s = format_key(key); 187 if (sshkey_is_cert(key)) 188 ca_s = format_key(key->cert->signature_key); 189 190 if (have_sig) { 191 debug3_f("%s have %s signature for %s%s%s", 192 method, pkalg, key_s, 193 ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s); 194 if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 || 195 (r = sshpkt_get_end(ssh)) != 0) 196 fatal_fr(r, "parse signature packet"); 197 if ((b = sshbuf_new()) == NULL) 198 fatal_f("sshbuf_new failed"); 199 if (ssh->compat & SSH_OLD_SESSIONID) { 200 if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0) 201 fatal_fr(r, "put old session id"); 202 } else { 203 if ((r = sshbuf_put_stringb(b, 204 ssh->kex->session_id)) != 0) 205 fatal_fr(r, "put session id"); 206 } 207 if (!authctxt->valid || authctxt->user == NULL) { 208 debug2_f("disabled because of invalid user"); 209 goto done; 210 } 211 /* reconstruct packet */ 212 xasprintf(&userstyle, "%s%s%s", authctxt->user, 213 authctxt->style ? ":" : "", 214 authctxt->style ? authctxt->style : ""); 215 if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || 216 (r = sshbuf_put_cstring(b, userstyle)) != 0 || 217 (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || 218 (r = sshbuf_put_cstring(b, method)) != 0 || 219 (r = sshbuf_put_u8(b, have_sig)) != 0 || 220 (r = sshbuf_put_cstring(b, pkalg)) != 0 || 221 (r = sshbuf_put_string(b, pkblob, blen)) != 0) 222 fatal_fr(r, "reconstruct %s packet", method); 223 if (hostbound && 224 (r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0) 225 fatal_fr(r, "reconstruct %s packet", method); 226 #ifdef DEBUG_PK 227 sshbuf_dump(b, stderr); 228 #endif 229 /* test for correct signature */ 230 authenticated = 0; 231 if (mm_user_key_allowed(ssh, pw, key, 1, &authopts) && 232 mm_sshkey_verify(key, sig, slen, 233 sshbuf_ptr(b), sshbuf_len(b), 234 (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL, 235 ssh->compat, &sig_details) == 0) { 236 authenticated = 1; 237 } 238 if (authenticated == 1 && sig_details != NULL) { 239 auth2_record_info(authctxt, "signature count = %u", 240 sig_details->sk_counter); 241 debug_f("sk_counter = %u, sk_flags = 0x%02x", 242 sig_details->sk_counter, sig_details->sk_flags); 243 req_presence = (options.pubkey_auth_options & 244 PUBKEYAUTH_TOUCH_REQUIRED) || 245 !authopts->no_require_user_presence; 246 if (req_presence && (sig_details->sk_flags & 247 SSH_SK_USER_PRESENCE_REQD) == 0) { 248 error("public key %s signature for %s%s from " 249 "%.128s port %d rejected: user presence " 250 "(authenticator touch) requirement " 251 "not met ", key_s, 252 authctxt->valid ? "" : "invalid user ", 253 authctxt->user, ssh_remote_ipaddr(ssh), 254 ssh_remote_port(ssh)); 255 authenticated = 0; 256 goto done; 257 } 258 req_verify = (options.pubkey_auth_options & 259 PUBKEYAUTH_VERIFY_REQUIRED) || 260 authopts->require_verify; 261 if (req_verify && (sig_details->sk_flags & 262 SSH_SK_USER_VERIFICATION_REQD) == 0) { 263 error("public key %s signature for %s%s from " 264 "%.128s port %d rejected: user " 265 "verification requirement not met ", key_s, 266 authctxt->valid ? "" : "invalid user ", 267 authctxt->user, ssh_remote_ipaddr(ssh), 268 ssh_remote_port(ssh)); 269 authenticated = 0; 270 goto done; 271 } 272 } 273 auth2_record_key(authctxt, authenticated, key); 274 } else { 275 debug_f("%s test pkalg %s pkblob %s%s%s", method, pkalg, key_s, 276 ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s); 277 278 if ((r = sshpkt_get_end(ssh)) != 0) 279 fatal_fr(r, "parse packet"); 280 281 if (!authctxt->valid || authctxt->user == NULL) { 282 debug2_f("disabled because of invalid user"); 283 goto done; 284 } 285 /* XXX fake reply and always send PK_OK ? */ 286 /* 287 * XXX this allows testing whether a user is allowed 288 * to login: if you happen to have a valid pubkey this 289 * message is sent. the message is NEVER sent at all 290 * if a user is not allowed to login. is this an 291 * issue? -markus 292 */ 293 if (mm_user_key_allowed(ssh, pw, key, 0, NULL)) { 294 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK)) 295 != 0 || 296 (r = sshpkt_put_cstring(ssh, pkalg)) != 0 || 297 (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 || 298 (r = sshpkt_send(ssh)) != 0 || 299 (r = ssh_packet_write_wait(ssh)) < 0) 300 fatal_fr(r, "send packet"); 301 authctxt->postponed = 1; 302 } 303 } 304 done: 305 if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) { 306 debug_f("key options inconsistent with existing"); 307 authenticated = 0; 308 } 309 debug2_f("authenticated %d pkalg %s", authenticated, pkalg); 310 311 sshbuf_free(b); 312 sshauthopt_free(authopts); 313 sshkey_free(key); 314 sshkey_free(hostkey); 315 free(userstyle); 316 free(pkalg); 317 free(pkblob); 318 free(key_s); 319 free(ca_s); 320 free(sig); 321 sshkey_sig_details_free(sig_details); 322 return authenticated; 323 } 324 325 static int 326 match_principals_file(struct passwd *pw, char *file, 327 struct sshkey_cert *cert, struct sshauthopt **authoptsp) 328 { 329 FILE *f; 330 int r, success = 0; 331 size_t i; 332 glob_t gl; 333 struct sshauthopt *opts = NULL; 334 335 if (authoptsp != NULL) 336 *authoptsp = NULL; 337 338 temporarily_use_uid(pw); 339 r = glob(file, 0, NULL, &gl); 340 restore_uid(); 341 if (r != 0) { 342 if (r != GLOB_NOMATCH) { 343 logit_f("glob \"%s\" failed", file); 344 } 345 return 0; 346 } else if (gl.gl_pathc > INT_MAX) { 347 fatal_f("too many glob results for \"%s\"", file); 348 } else if (gl.gl_pathc > 1) { 349 debug2_f("glob \"%s\" returned %zu matches", file, 350 gl.gl_pathc); 351 } 352 for (i = 0; !success && i < gl.gl_pathc; i++) { 353 temporarily_use_uid(pw); 354 debug("trying authorized principals file %s", file); 355 if ((f = auth_openprincipals(gl.gl_pathv[i], pw, 356 options.strict_modes)) == NULL) { 357 restore_uid(); 358 continue; 359 } 360 success = auth_process_principals(f, gl.gl_pathv[i], 361 cert, &opts); 362 fclose(f); 363 restore_uid(); 364 if (!success) { 365 sshauthopt_free(opts); 366 opts = NULL; 367 } 368 } 369 globfree(&gl); 370 if (success && authoptsp != NULL) { 371 *authoptsp = opts; 372 opts = NULL; 373 } 374 sshauthopt_free(opts); 375 return success; 376 } 377 378 /* 379 * Checks whether principal is allowed in output of command. 380 * returns 1 if the principal is allowed or 0 otherwise. 381 */ 382 static int 383 match_principals_command(struct passwd *user_pw, const struct sshkey *key, 384 const char *conn_id, const char *rdomain, struct sshauthopt **authoptsp) 385 { 386 struct passwd *runas_pw = NULL; 387 const struct sshkey_cert *cert = key->cert; 388 FILE *f = NULL; 389 int r, ok, found_principal = 0; 390 int i, ac = 0, uid_swapped = 0; 391 pid_t pid; 392 char *tmp, *username = NULL, *command = NULL, **av = NULL; 393 char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL; 394 char serial_s[32], uidstr[32]; 395 void (*osigchld)(int); 396 397 if (authoptsp != NULL) 398 *authoptsp = NULL; 399 if (options.authorized_principals_command == NULL) 400 return 0; 401 if (options.authorized_principals_command_user == NULL) { 402 error("No user for AuthorizedPrincipalsCommand specified, " 403 "skipping"); 404 return 0; 405 } 406 407 /* 408 * NB. all returns later this function should go via "out" to 409 * ensure the original SIGCHLD handler is restored properly. 410 */ 411 osigchld = ssh_signal(SIGCHLD, SIG_DFL); 412 413 /* Prepare and verify the user for the command */ 414 username = percent_expand(options.authorized_principals_command_user, 415 "u", user_pw->pw_name, (char *)NULL); 416 runas_pw = getpwnam(username); 417 if (runas_pw == NULL) { 418 error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", 419 username, strerror(errno)); 420 goto out; 421 } 422 423 /* Turn the command into an argument vector */ 424 if (argv_split(options.authorized_principals_command, 425 &ac, &av, 0) != 0) { 426 error("AuthorizedPrincipalsCommand \"%s\" contains " 427 "invalid quotes", options.authorized_principals_command); 428 goto out; 429 } 430 if (ac == 0) { 431 error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments", 432 options.authorized_principals_command); 433 goto out; 434 } 435 if ((ca_fp = sshkey_fingerprint(cert->signature_key, 436 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 437 error_f("sshkey_fingerprint failed"); 438 goto out; 439 } 440 if ((key_fp = sshkey_fingerprint(key, 441 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 442 error_f("sshkey_fingerprint failed"); 443 goto out; 444 } 445 if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) { 446 error_fr(r, "sshkey_to_base64 failed"); 447 goto out; 448 } 449 if ((r = sshkey_to_base64(key, &keytext)) != 0) { 450 error_fr(r, "sshkey_to_base64 failed"); 451 goto out; 452 } 453 snprintf(serial_s, sizeof(serial_s), "%llu", 454 (unsigned long long)cert->serial); 455 snprintf(uidstr, sizeof(uidstr), "%llu", 456 (unsigned long long)user_pw->pw_uid); 457 for (i = 1; i < ac; i++) { 458 tmp = percent_expand(av[i], 459 "C", conn_id, 460 "D", rdomain, 461 "U", uidstr, 462 "u", user_pw->pw_name, 463 "h", user_pw->pw_dir, 464 "t", sshkey_ssh_name(key), 465 "T", sshkey_ssh_name(cert->signature_key), 466 "f", key_fp, 467 "F", ca_fp, 468 "k", keytext, 469 "K", catext, 470 "i", cert->key_id, 471 "s", serial_s, 472 (char *)NULL); 473 if (tmp == NULL) 474 fatal_f("percent_expand failed"); 475 free(av[i]); 476 av[i] = tmp; 477 } 478 /* Prepare a printable command for logs, etc. */ 479 command = argv_assemble(ac, av); 480 481 if ((pid = subprocess("AuthorizedPrincipalsCommand", command, 482 ac, av, &f, 483 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, 484 runas_pw, temporarily_use_uid, restore_uid)) == 0) 485 goto out; 486 487 uid_swapped = 1; 488 temporarily_use_uid(runas_pw); 489 490 ok = auth_process_principals(f, "(command)", cert, authoptsp); 491 492 fclose(f); 493 f = NULL; 494 495 if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0) 496 goto out; 497 498 /* Read completed successfully */ 499 found_principal = ok; 500 out: 501 if (f != NULL) 502 fclose(f); 503 ssh_signal(SIGCHLD, osigchld); 504 for (i = 0; i < ac; i++) 505 free(av[i]); 506 free(av); 507 if (uid_swapped) 508 restore_uid(); 509 free(command); 510 free(username); 511 free(ca_fp); 512 free(key_fp); 513 free(catext); 514 free(keytext); 515 return found_principal; 516 } 517 518 /* Authenticate a certificate key against TrustedUserCAKeys */ 519 static int 520 user_cert_trusted_ca(struct passwd *pw, struct sshkey *key, 521 const char *remote_ip, const char *remote_host, 522 const char *conn_id, const char *rdomain, struct sshauthopt **authoptsp) 523 { 524 char *ca_fp, *principals_file = NULL; 525 const char *reason; 526 struct sshauthopt *principals_opts = NULL, *cert_opts = NULL; 527 struct sshauthopt *final_opts = NULL; 528 int r, ret = 0, found_principal = 0, use_authorized_principals; 529 530 if (authoptsp != NULL) 531 *authoptsp = NULL; 532 533 if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL) 534 return 0; 535 536 if ((ca_fp = sshkey_fingerprint(key->cert->signature_key, 537 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 538 return 0; 539 540 if ((r = sshkey_in_file(key->cert->signature_key, 541 options.trusted_user_ca_keys, 1, 0)) != 0) { 542 debug2_fr(r, "CA %s %s is not listed in %s", 543 sshkey_type(key->cert->signature_key), ca_fp, 544 options.trusted_user_ca_keys); 545 goto out; 546 } 547 /* 548 * If AuthorizedPrincipals is in use, then compare the certificate 549 * principals against the names in that file rather than matching 550 * against the username. 551 */ 552 if ((principals_file = authorized_principals_file(pw)) != NULL) { 553 if (match_principals_file(pw, principals_file, 554 key->cert, &principals_opts)) 555 found_principal = 1; 556 } 557 /* Try querying command if specified */ 558 if (!found_principal && match_principals_command(pw, key, 559 conn_id, rdomain, &principals_opts)) 560 found_principal = 1; 561 /* If principals file or command is specified, then require a match */ 562 use_authorized_principals = principals_file != NULL || 563 options.authorized_principals_command != NULL; 564 if (!found_principal && use_authorized_principals) { 565 reason = "Certificate does not contain an authorized principal"; 566 goto fail_reason; 567 } 568 if (use_authorized_principals && principals_opts == NULL) 569 fatal_f("internal error: missing principals_opts"); 570 if (sshkey_cert_check_authority_now(key, 0, 0, 571 use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) 572 goto fail_reason; 573 574 /* Check authority from options in key and from principals file/cmd */ 575 if ((cert_opts = sshauthopt_from_cert(key)) == NULL) { 576 reason = "Invalid certificate options"; 577 goto fail_reason; 578 } 579 if (auth_authorise_keyopts(pw, cert_opts, 0, 580 remote_ip, remote_host, "cert") != 0) { 581 reason = "Refused by certificate options"; 582 goto fail_reason; 583 } 584 if (principals_opts == NULL) { 585 final_opts = cert_opts; 586 cert_opts = NULL; 587 } else { 588 if (auth_authorise_keyopts(pw, principals_opts, 0, 589 remote_ip, remote_host, "principals") != 0) { 590 reason = "Refused by certificate principals options"; 591 goto fail_reason; 592 } 593 if ((final_opts = sshauthopt_merge(principals_opts, 594 cert_opts, &reason)) == NULL) { 595 fail_reason: 596 error("Refusing certificate ID \"%s\" serial=%llu " 597 "signed by %s CA %s: %s", key->cert->key_id, 598 (unsigned long long)key->cert->serial, 599 sshkey_type(key->cert->signature_key), ca_fp, 600 reason); 601 auth_debug_add("Refused Certificate ID \"%s\" " 602 "serial=%llu: %s", key->cert->key_id, 603 (unsigned long long)key->cert->serial, reason); 604 goto out; 605 } 606 } 607 608 /* Success */ 609 verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " 610 "%s CA %s via %s", key->cert->key_id, 611 (unsigned long long)key->cert->serial, 612 sshkey_type(key->cert->signature_key), ca_fp, 613 options.trusted_user_ca_keys); 614 if (authoptsp != NULL) { 615 *authoptsp = final_opts; 616 final_opts = NULL; 617 } 618 ret = 1; 619 out: 620 sshauthopt_free(principals_opts); 621 sshauthopt_free(cert_opts); 622 sshauthopt_free(final_opts); 623 free(principals_file); 624 free(ca_fp); 625 return ret; 626 } 627 628 /* 629 * Checks whether key is allowed in file. 630 * returns 1 if the key is allowed or 0 otherwise. 631 */ 632 static int 633 user_key_allowed2(struct passwd *pw, struct sshkey *key, 634 char *file, const char *remote_ip, const char *remote_host, 635 struct sshauthopt **authoptsp) 636 { 637 FILE *f; 638 int found_key = 0; 639 640 if (authoptsp != NULL) 641 *authoptsp = NULL; 642 643 /* Temporarily use the user's uid. */ 644 temporarily_use_uid(pw); 645 646 debug("trying public key file %s", file); 647 if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { 648 found_key = auth_check_authkeys_file(pw, f, file, 649 key, remote_ip, remote_host, authoptsp); 650 fclose(f); 651 } 652 653 restore_uid(); 654 return found_key; 655 } 656 657 /* 658 * Checks whether key is allowed in output of command. 659 * returns 1 if the key is allowed or 0 otherwise. 660 */ 661 static int 662 user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key, 663 const char *remote_ip, const char *remote_host, 664 const char *conn_id, const char *rdomain, struct sshauthopt **authoptsp) 665 { 666 struct passwd *runas_pw = NULL; 667 FILE *f = NULL; 668 int r, ok, found_key = 0; 669 int i, uid_swapped = 0, ac = 0; 670 pid_t pid; 671 char *username = NULL, *key_fp = NULL, *keytext = NULL; 672 char uidstr[32], *tmp, *command = NULL, **av = NULL; 673 void (*osigchld)(int); 674 675 if (authoptsp != NULL) 676 *authoptsp = NULL; 677 if (options.authorized_keys_command == NULL) 678 return 0; 679 if (options.authorized_keys_command_user == NULL) { 680 error("No user for AuthorizedKeysCommand specified, skipping"); 681 return 0; 682 } 683 684 /* 685 * NB. all returns later this function should go via "out" to 686 * ensure the original SIGCHLD handler is restored properly. 687 */ 688 osigchld = ssh_signal(SIGCHLD, SIG_DFL); 689 690 /* Prepare and verify the user for the command */ 691 username = percent_expand(options.authorized_keys_command_user, 692 "u", user_pw->pw_name, (char *)NULL); 693 runas_pw = getpwnam(username); 694 if (runas_pw == NULL) { 695 error("AuthorizedKeysCommandUser \"%s\" not found: %s", 696 username, strerror(errno)); 697 goto out; 698 } 699 700 /* Prepare AuthorizedKeysCommand */ 701 if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, 702 SSH_FP_DEFAULT)) == NULL) { 703 error_f("sshkey_fingerprint failed"); 704 goto out; 705 } 706 if ((r = sshkey_to_base64(key, &keytext)) != 0) { 707 error_fr(r, "sshkey_to_base64 failed"); 708 goto out; 709 } 710 711 /* Turn the command into an argument vector */ 712 if (argv_split(options.authorized_keys_command, &ac, &av, 0) != 0) { 713 error("AuthorizedKeysCommand \"%s\" contains invalid quotes", 714 options.authorized_keys_command); 715 goto out; 716 } 717 if (ac == 0) { 718 error("AuthorizedKeysCommand \"%s\" yielded no arguments", 719 options.authorized_keys_command); 720 goto out; 721 } 722 snprintf(uidstr, sizeof(uidstr), "%llu", 723 (unsigned long long)user_pw->pw_uid); 724 for (i = 1; i < ac; i++) { 725 tmp = percent_expand(av[i], 726 "C", conn_id, 727 "D", rdomain, 728 "U", uidstr, 729 "u", user_pw->pw_name, 730 "h", user_pw->pw_dir, 731 "t", sshkey_ssh_name(key), 732 "f", key_fp, 733 "k", keytext, 734 (char *)NULL); 735 if (tmp == NULL) 736 fatal_f("percent_expand failed"); 737 free(av[i]); 738 av[i] = tmp; 739 } 740 /* Prepare a printable command for logs, etc. */ 741 command = argv_assemble(ac, av); 742 743 /* 744 * If AuthorizedKeysCommand was run without arguments 745 * then fall back to the old behaviour of passing the 746 * target username as a single argument. 747 */ 748 if (ac == 1) { 749 av = xreallocarray(av, ac + 2, sizeof(*av)); 750 av[1] = xstrdup(user_pw->pw_name); 751 av[2] = NULL; 752 /* Fix up command too, since it is used in log messages */ 753 free(command); 754 xasprintf(&command, "%s %s", av[0], av[1]); 755 } 756 757 if ((pid = subprocess("AuthorizedKeysCommand", command, 758 ac, av, &f, 759 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, 760 runas_pw, temporarily_use_uid, restore_uid)) == 0) 761 goto out; 762 763 uid_swapped = 1; 764 temporarily_use_uid(runas_pw); 765 766 ok = auth_check_authkeys_file(user_pw, f, 767 options.authorized_keys_command, key, remote_ip, 768 remote_host, authoptsp); 769 770 fclose(f); 771 f = NULL; 772 773 if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0) 774 goto out; 775 776 /* Read completed successfully */ 777 found_key = ok; 778 out: 779 if (f != NULL) 780 fclose(f); 781 ssh_signal(SIGCHLD, osigchld); 782 for (i = 0; i < ac; i++) 783 free(av[i]); 784 free(av); 785 if (uid_swapped) 786 restore_uid(); 787 free(command); 788 free(username); 789 free(key_fp); 790 free(keytext); 791 return found_key; 792 } 793 794 /* 795 * Check whether key authenticates and authorises the user. 796 */ 797 int 798 user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, 799 int auth_attempt, struct sshauthopt **authoptsp) 800 { 801 u_int success = 0, i, j; 802 char *file = NULL, *conn_id; 803 struct sshauthopt *opts = NULL; 804 const char *rdomain, *remote_ip, *remote_host; 805 806 if (authoptsp != NULL) 807 *authoptsp = NULL; 808 809 if (auth_key_is_revoked(key)) 810 return 0; 811 if (sshkey_is_cert(key) && 812 auth_key_is_revoked(key->cert->signature_key)) 813 return 0; 814 815 if ((rdomain = ssh_packet_rdomain_in(ssh)) == NULL) 816 rdomain = ""; 817 remote_ip = ssh_remote_ipaddr(ssh); 818 remote_host = auth_get_canonical_hostname(ssh, options.use_dns); 819 xasprintf(&conn_id, "%s %d %s %d", 820 ssh_local_ipaddr(ssh), ssh_local_port(ssh), 821 remote_ip, ssh_remote_port(ssh)); 822 823 for (i = 0; !success && i < options.num_authkeys_files; i++) { 824 int r; 825 glob_t gl; 826 827 if (strcasecmp(options.authorized_keys_files[i], "none") == 0) 828 continue; 829 file = expand_authorized_keys( 830 options.authorized_keys_files[i], pw); 831 temporarily_use_uid(pw); 832 r = glob(file, 0, NULL, &gl); 833 restore_uid(); 834 if (r != 0) { 835 if (r != GLOB_NOMATCH) { 836 logit_f("glob \"%s\" failed", file); 837 } 838 free(file); 839 file = NULL; 840 continue; 841 } else if (gl.gl_pathc > INT_MAX) { 842 fatal_f("too many glob results for \"%s\"", file); 843 } else if (gl.gl_pathc > 1) { 844 debug2_f("glob \"%s\" returned %zu matches", file, 845 gl.gl_pathc); 846 } 847 for (j = 0; !success && j < gl.gl_pathc; j++) { 848 success = user_key_allowed2(pw, key, gl.gl_pathv[j], 849 remote_ip, remote_host, &opts); 850 if (!success) { 851 sshauthopt_free(opts); 852 opts = NULL; 853 } 854 } 855 free(file); 856 file = NULL; 857 globfree(&gl); 858 } 859 if (success) 860 goto out; 861 862 if ((success = user_cert_trusted_ca(pw, key, remote_ip, remote_host, 863 conn_id, rdomain, &opts)) != 0) 864 goto out; 865 sshauthopt_free(opts); 866 opts = NULL; 867 868 if ((success = user_key_command_allowed2(pw, key, remote_ip, 869 remote_host, conn_id, rdomain, &opts)) != 0) 870 goto out; 871 sshauthopt_free(opts); 872 opts = NULL; 873 874 out: 875 free(conn_id); 876 if (success && authoptsp != NULL) { 877 *authoptsp = opts; 878 opts = NULL; 879 } 880 sshauthopt_free(opts); 881 return success; 882 } 883 884 Authmethod method_pubkey = { 885 &methodcfg_pubkey, 886 userauth_pubkey, 887 }; 888