1*79eb0734Srin /* $NetBSD: auth2.c,v 1.32 2024/10/09 01:49:20 rin Exp $ */ 21c7715ddSchristos /* $OpenBSD: auth2.c,v 1.169 2024/05/17 00:30:23 djm Exp $ */ 3a629fefcSchristos 4ca32bd8dSchristos /* 5ca32bd8dSchristos * Copyright (c) 2000 Markus Friedl. All rights reserved. 6ca32bd8dSchristos * 7ca32bd8dSchristos * Redistribution and use in source and binary forms, with or without 8ca32bd8dSchristos * modification, are permitted provided that the following conditions 9ca32bd8dSchristos * are met: 10ca32bd8dSchristos * 1. Redistributions of source code must retain the above copyright 11ca32bd8dSchristos * notice, this list of conditions and the following disclaimer. 12ca32bd8dSchristos * 2. Redistributions in binary form must reproduce the above copyright 13ca32bd8dSchristos * notice, this list of conditions and the following disclaimer in the 14ca32bd8dSchristos * documentation and/or other materials provided with the distribution. 15ca32bd8dSchristos * 16ca32bd8dSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17ca32bd8dSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18ca32bd8dSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19ca32bd8dSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20ca32bd8dSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21ca32bd8dSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22ca32bd8dSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23ca32bd8dSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24ca32bd8dSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25ca32bd8dSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26ca32bd8dSchristos */ 27ca32bd8dSchristos 28313c6c94Schristos #include "includes.h" 29*79eb0734Srin __RCSID("$NetBSD: auth2.c,v 1.32 2024/10/09 01:49:20 rin Exp $"); 30cd4ada6aSchristos 31ca32bd8dSchristos #include <sys/types.h> 32ca32bd8dSchristos #include <sys/stat.h> 33ca32bd8dSchristos #include <sys/uio.h> 34ca32bd8dSchristos 35ca32bd8dSchristos #include <fcntl.h> 367a183406Schristos #include <limits.h> 37ca32bd8dSchristos #include <pwd.h> 38ca32bd8dSchristos #include <stdarg.h> 3947dc7704Schristos #include <string.h> 40ca32bd8dSchristos #include <unistd.h> 41aa36fcacSchristos #include <time.h> 42ca32bd8dSchristos 43cd4ada6aSchristos #include "stdlib.h" 44ca32bd8dSchristos #include "atomicio.h" 45ca32bd8dSchristos #include "xmalloc.h" 46ca32bd8dSchristos #include "ssh2.h" 47ca32bd8dSchristos #include "packet.h" 48ca32bd8dSchristos #include "log.h" 4955a4608bSchristos #include "sshbuf.h" 508a4530f9Schristos #include "misc.h" 51ca32bd8dSchristos #include "servconf.h" 5255a4608bSchristos #include "sshkey.h" 53ca32bd8dSchristos #include "hostfile.h" 54ca32bd8dSchristos #include "auth.h" 55ca32bd8dSchristos #include "dispatch.h" 56ca32bd8dSchristos #include "pathnames.h" 57313c6c94Schristos #include "canohost.h" 5872106c52Schristos #include "pfilter.h" 59313c6c94Schristos 60ca32bd8dSchristos #ifdef GSSAPI 61ca32bd8dSchristos #include "ssh-gss.h" 62ca32bd8dSchristos #endif 63313c6c94Schristos 64ca32bd8dSchristos #include "monitor_wrap.h" 657a183406Schristos #include "ssherr.h" 6655a4608bSchristos #include "digest.h" 67514b5d45Schristos #include "kex.h" 68ca32bd8dSchristos 69ca32bd8dSchristos /* import */ 70ca32bd8dSchristos extern ServerOptions options; 7155a4608bSchristos extern struct sshbuf *loginmsg; 72ca32bd8dSchristos 73ca32bd8dSchristos /* methods */ 74ca32bd8dSchristos 75ca32bd8dSchristos extern Authmethod method_none; 76ca32bd8dSchristos extern Authmethod method_pubkey; 77ca32bd8dSchristos extern Authmethod method_passwd; 78ca32bd8dSchristos extern Authmethod method_kbdint; 79ca32bd8dSchristos extern Authmethod method_hostbased; 80313c6c94Schristos #ifdef KRB5 81313c6c94Schristos extern Authmethod method_kerberos; 82313c6c94Schristos #endif 83ca32bd8dSchristos #ifdef GSSAPI 84ca32bd8dSchristos extern Authmethod method_gssapi; 85ca32bd8dSchristos #endif 86ca32bd8dSchristos 87313c6c94Schristos static int log_flag = 0; 88313c6c94Schristos 89ca32bd8dSchristos Authmethod *authmethods[] = { 90ca32bd8dSchristos &method_none, 91ca32bd8dSchristos &method_pubkey, 92ca32bd8dSchristos #ifdef GSSAPI 93ca32bd8dSchristos &method_gssapi, 94ca32bd8dSchristos #endif 95ca32bd8dSchristos &method_passwd, 96ca32bd8dSchristos &method_kbdint, 97ca32bd8dSchristos &method_hostbased, 98313c6c94Schristos #ifdef KRB5 99313c6c94Schristos &method_kerberos, 100313c6c94Schristos #endif 101ca32bd8dSchristos NULL 102ca32bd8dSchristos }; 103ca32bd8dSchristos 104ca32bd8dSchristos /* protocol */ 105ca32bd8dSchristos 1067a183406Schristos static int input_service_request(int, u_int32_t, struct ssh *); 1077a183406Schristos static int input_userauth_request(int, u_int32_t, struct ssh *); 108ca32bd8dSchristos 109ca32bd8dSchristos /* helper */ 110a03ec00cSchristos static Authmethod *authmethod_byname(const char *); 111ce11a51fSchristos static Authmethod *authmethod_lookup(Authctxt *, const char *); 112ce11a51fSchristos static char *authmethods_get(Authctxt *authctxt); 11300a838c4Schristos 11400a838c4Schristos #define MATCH_NONE 0 /* method or submethod mismatch */ 11500a838c4Schristos #define MATCH_METHOD 1 /* method matches (no submethod specified) */ 11600a838c4Schristos #define MATCH_BOTH 2 /* method and submethod match */ 11700a838c4Schristos #define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ 11800a838c4Schristos static int list_starts_with(const char *, const char *, const char *); 119ca32bd8dSchristos 120ca32bd8dSchristos char * 121ca32bd8dSchristos auth2_read_banner(void) 122ca32bd8dSchristos { 123ca32bd8dSchristos struct stat st; 124ca32bd8dSchristos char *banner = NULL; 125ca32bd8dSchristos size_t len, n; 126ca32bd8dSchristos int fd; 127ca32bd8dSchristos 128ca32bd8dSchristos if ((fd = open(options.banner, O_RDONLY)) == -1) 129ca32bd8dSchristos return (NULL); 130ca32bd8dSchristos if (fstat(fd, &st) == -1) { 131ca32bd8dSchristos close(fd); 132ca32bd8dSchristos return (NULL); 133ca32bd8dSchristos } 134091c4109Schristos if (st.st_size <= 0 || st.st_size > 1*1024*1024) { 135ca32bd8dSchristos close(fd); 136ca32bd8dSchristos return (NULL); 137ca32bd8dSchristos } 138ca32bd8dSchristos 139ca32bd8dSchristos len = (size_t)st.st_size; /* truncate */ 140ca32bd8dSchristos banner = xmalloc(len + 1); 141ca32bd8dSchristos n = atomicio(read, fd, banner, len); 142ca32bd8dSchristos close(fd); 143ca32bd8dSchristos 144ca32bd8dSchristos if (n != len) { 14500a838c4Schristos free(banner); 146ca32bd8dSchristos return (NULL); 147ca32bd8dSchristos } 148ca32bd8dSchristos banner[n] = '\0'; 149ca32bd8dSchristos 150ca32bd8dSchristos return (banner); 151ca32bd8dSchristos } 152ca32bd8dSchristos 153ce11a51fSchristos static void 154aa36fcacSchristos userauth_send_banner(struct ssh *ssh, const char *msg) 155313c6c94Schristos { 156aa36fcacSchristos int r; 157aa36fcacSchristos 158aa36fcacSchristos if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_BANNER)) != 0 || 159aa36fcacSchristos (r = sshpkt_put_cstring(ssh, msg)) != 0 || 160aa36fcacSchristos (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language, unused */ 161aa36fcacSchristos (r = sshpkt_send(ssh)) != 0) 162a244501eSkre fatal_fr(r, "send packet"); 163313c6c94Schristos debug("%s: sent", __func__); 164313c6c94Schristos } 165313c6c94Schristos 166ca32bd8dSchristos static void 167aa36fcacSchristos userauth_banner(struct ssh *ssh) 168ca32bd8dSchristos { 169ca32bd8dSchristos char *banner = NULL; 170ca32bd8dSchristos 171ffae97bbSchristos if (options.banner == NULL) 172ca32bd8dSchristos return; 173ca32bd8dSchristos 1741c7715ddSchristos if ((banner = mm_auth2_read_banner()) == NULL) 175ca32bd8dSchristos goto done; 176aa36fcacSchristos userauth_send_banner(ssh, banner); 177ca32bd8dSchristos 178ca32bd8dSchristos done: 17900a838c4Schristos free(banner); 180ca32bd8dSchristos } 181ca32bd8dSchristos 182ca32bd8dSchristos /* 183ca32bd8dSchristos * loop until authctxt->success == TRUE 184ca32bd8dSchristos */ 185ca32bd8dSchristos void 186aa36fcacSchristos do_authentication2(struct ssh *ssh) 187ca32bd8dSchristos { 188aa36fcacSchristos Authctxt *authctxt = ssh->authctxt; 189aa36fcacSchristos 1907a183406Schristos ssh_dispatch_init(ssh, &dispatch_protocol_error); 191514b5d45Schristos if (ssh->kex->ext_info_c) 192514b5d45Schristos ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info); 1937a183406Schristos ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request); 1947a183406Schristos ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success); 1957a183406Schristos ssh->authctxt = NULL; 196ca32bd8dSchristos } 197ca32bd8dSchristos 198e4d43b82Schristos static int 1997a183406Schristos input_service_request(int type, u_int32_t seq, struct ssh *ssh) 200ca32bd8dSchristos { 2017a183406Schristos Authctxt *authctxt = ssh->authctxt; 202aa36fcacSchristos char *service = NULL; 203aa36fcacSchristos int r, acceptit = 0; 204aa36fcacSchristos 205aa36fcacSchristos if ((r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || 206aa36fcacSchristos (r = sshpkt_get_end(ssh)) != 0) 207aa36fcacSchristos goto out; 208ca32bd8dSchristos 209ca32bd8dSchristos if (authctxt == NULL) 210ca32bd8dSchristos fatal("input_service_request: no authctxt"); 211ca32bd8dSchristos 212ca32bd8dSchristos if (strcmp(service, "ssh-userauth") == 0) { 213ca32bd8dSchristos if (!authctxt->success) { 214ca32bd8dSchristos acceptit = 1; 215ca32bd8dSchristos /* now we can handle user-auth requests */ 216aa36fcacSchristos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, 217aa36fcacSchristos &input_userauth_request); 218ca32bd8dSchristos } 219ca32bd8dSchristos } 220ca32bd8dSchristos /* XXX all other service requests are denied */ 221ca32bd8dSchristos 222ca32bd8dSchristos if (acceptit) { 223aa36fcacSchristos if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_ACCEPT)) != 0 || 224aa36fcacSchristos (r = sshpkt_put_cstring(ssh, service)) != 0 || 225aa36fcacSchristos (r = sshpkt_send(ssh)) != 0 || 226aa36fcacSchristos (r = ssh_packet_write_wait(ssh)) < 0) 227aa36fcacSchristos goto out; 228ca32bd8dSchristos } else { 229ca32bd8dSchristos debug("bad service request %s", service); 230aa36fcacSchristos ssh_packet_disconnect(ssh, "bad service request %s", service); 231ca32bd8dSchristos } 232514b5d45Schristos ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &dispatch_protocol_error); 233aa36fcacSchristos r = 0; 234aa36fcacSchristos out: 23500a838c4Schristos free(service); 2368db691beSchristos return r; 237ca32bd8dSchristos } 238ca32bd8dSchristos 23955a4608bSchristos #define MIN_FAIL_DELAY_SECONDS 0.005 240a629fefcSchristos #define MAX_FAIL_DELAY_SECONDS 5.0 24155a4608bSchristos static double 24255a4608bSchristos user_specific_delay(const char *user) 24355a4608bSchristos { 24455a4608bSchristos char b[512]; 24555a4608bSchristos size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512); 24655a4608bSchristos u_char *hash = xmalloc(len); 24755a4608bSchristos double delay; 24855a4608bSchristos 24955a4608bSchristos (void)snprintf(b, sizeof b, "%llu%s", 25055a4608bSchristos (unsigned long long)options.timing_secret, user); 25155a4608bSchristos if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0) 25217418e98Schristos fatal_f("ssh_digest_memory"); 25355a4608bSchristos /* 0-4.2 ms of delay */ 25455a4608bSchristos delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000; 25555a4608bSchristos freezero(hash, len); 25617418e98Schristos debug3_f("user specific delay %0.3lfms", delay/1000); 25755a4608bSchristos return MIN_FAIL_DELAY_SECONDS + delay; 25855a4608bSchristos } 25955a4608bSchristos 26055a4608bSchristos static void 26155a4608bSchristos ensure_minimum_time_since(double start, double seconds) 26255a4608bSchristos { 26355a4608bSchristos struct timespec ts; 26455a4608bSchristos double elapsed = monotime_double() - start, req = seconds, remain; 26555a4608bSchristos 266a629fefcSchristos if (elapsed > MAX_FAIL_DELAY_SECONDS) { 267a629fefcSchristos debug3_f("elapsed %0.3lfms exceeded the max delay " 268a629fefcSchristos "requested %0.3lfms)", elapsed*1000, req*1000); 269a629fefcSchristos return; 270a629fefcSchristos } 271a629fefcSchristos 27255a4608bSchristos /* if we've already passed the requested time, scale up */ 27355a4608bSchristos while ((remain = seconds - elapsed) < 0.0) 27455a4608bSchristos seconds *= 2; 27555a4608bSchristos 27655a4608bSchristos ts.tv_sec = remain; 27755a4608bSchristos ts.tv_nsec = (remain - ts.tv_sec) * 1000000000; 27817418e98Schristos debug3_f("elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)", 27917418e98Schristos elapsed*1000, remain*1000, req*1000); 28055a4608bSchristos nanosleep(&ts, NULL); 28155a4608bSchristos } 28255a4608bSchristos 283e4d43b82Schristos static int 2847a183406Schristos input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) 285ca32bd8dSchristos { 2867a183406Schristos Authctxt *authctxt = ssh->authctxt; 287ca32bd8dSchristos Authmethod *m = NULL; 288aa36fcacSchristos char *user = NULL, *service = NULL, *method = NULL, *style = NULL; 289aa36fcacSchristos int r, authenticated = 0; 29055a4608bSchristos double tstart = monotime_double(); 291ca32bd8dSchristos 292ca32bd8dSchristos if (authctxt == NULL) 293ca32bd8dSchristos fatal("input_userauth_request: no authctxt"); 294ca32bd8dSchristos 295aa36fcacSchristos if ((r = sshpkt_get_cstring(ssh, &user, NULL)) != 0 || 296aa36fcacSchristos (r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || 297aa36fcacSchristos (r = sshpkt_get_cstring(ssh, &method, NULL)) != 0) 298aa36fcacSchristos goto out; 299ca32bd8dSchristos debug("userauth-request for user %s service %s method %s", user, service, method); 300313c6c94Schristos if (!log_flag) { 301313c6c94Schristos logit("SSH: Server;Ltype: Authname;Remote: %s-%d;Name: %s", 3025101d403Schristos ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), user); 303313c6c94Schristos log_flag = 1; 304313c6c94Schristos } 305ca32bd8dSchristos debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); 306ca32bd8dSchristos 307ca32bd8dSchristos if ((style = strchr(user, ':')) != NULL) 308ca32bd8dSchristos *style++ = 0; 309ca32bd8dSchristos 310a03ec00cSchristos if (authctxt->attempt >= 1024) 311a03ec00cSchristos auth_maxtries_exceeded(ssh); 312ca32bd8dSchristos if (authctxt->attempt++ == 0) { 313ca32bd8dSchristos /* setup auth context */ 3141c7715ddSchristos authctxt->pw = mm_getpwnamallow(ssh, user); 315ca32bd8dSchristos if (authctxt->pw && strcmp(service, "ssh-connection")==0) { 316ca32bd8dSchristos authctxt->valid = 1; 31717418e98Schristos debug2_f("setting up authctxt for %s", user); 318ca32bd8dSchristos } else { 319a03ec00cSchristos authctxt->valid = 0; 32041768fc1Schristos /* Invalid user, fake password information */ 321ca32bd8dSchristos authctxt->pw = fakepw(); 322ca32bd8dSchristos } 323313c6c94Schristos #ifdef USE_PAM 324313c6c94Schristos if (options.use_pam) 3251c7715ddSchristos mm_start_pam(ssh); 326313c6c94Schristos #endif 32741768fc1Schristos ssh_packet_set_log_preamble(ssh, "%suser %s", 32841768fc1Schristos authctxt->valid ? "authenticating " : "invalid ", user); 3291c7715ddSchristos setproctitle("%s [net]", authctxt->valid ? user : "unknown"); 3301c7715ddSchristos authctxt->user = xstrdup(user); 331ca32bd8dSchristos authctxt->service = xstrdup(service); 332ca32bd8dSchristos authctxt->style = style ? xstrdup(style) : NULL; 333ca32bd8dSchristos mm_inform_authserv(service, style); 334aa36fcacSchristos userauth_banner(ssh); 335514b5d45Schristos if ((r = kex_server_update_ext_info(ssh)) != 0) 336514b5d45Schristos fatal_fr(r, "kex_server_update_ext_info failed"); 337ce11a51fSchristos if (auth2_setup_methods_lists(authctxt) != 0) 338aa36fcacSchristos ssh_packet_disconnect(ssh, 339aa36fcacSchristos "no authentication methods enabled"); 340ca32bd8dSchristos } else if (strcmp(user, authctxt->user) != 0 || 341ca32bd8dSchristos strcmp(service, authctxt->service) != 0) { 342aa36fcacSchristos ssh_packet_disconnect(ssh, "Change of username or service " 343aa36fcacSchristos "not allowed: (%s,%s) -> (%s,%s)", 344ca32bd8dSchristos authctxt->user, authctxt->service, user, service); 345ca32bd8dSchristos } 346ca32bd8dSchristos /* reset state */ 3477a183406Schristos auth2_challenge_stop(ssh); 348ca32bd8dSchristos 349ca32bd8dSchristos #ifdef GSSAPI 350ca32bd8dSchristos /* XXX move to auth2_gssapi_stop() */ 3517a183406Schristos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 3527a183406Schristos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 353ca32bd8dSchristos #endif 354ca32bd8dSchristos 3557a183406Schristos auth2_authctxt_reset_info(authctxt); 356ca32bd8dSchristos authctxt->postponed = 0; 3576f47b660Schristos authctxt->server_caused_failure = 0; 358ca32bd8dSchristos 359ca32bd8dSchristos /* try to authenticate user */ 360ce11a51fSchristos m = authmethod_lookup(authctxt, method); 361ca32bd8dSchristos if (m != NULL && authctxt->failures < options.max_authtries) { 362ca32bd8dSchristos debug2("input_userauth_request: try method %s", method); 363a03ec00cSchristos authenticated = m->userauth(ssh, method); 364ca32bd8dSchristos } 365a629fefcSchristos if (!authctxt->authenticated && strcmp(method, "none") != 0) 36655a4608bSchristos ensure_minimum_time_since(tstart, 36755a4608bSchristos user_specific_delay(authctxt->user)); 3687a183406Schristos userauth_finish(ssh, authenticated, method, NULL); 369aa36fcacSchristos r = 0; 370aa36fcacSchristos out: 37100a838c4Schristos free(service); 37200a838c4Schristos free(user); 37300a838c4Schristos free(method); 374aa36fcacSchristos return r; 375ca32bd8dSchristos } 376ca32bd8dSchristos 377ca32bd8dSchristos void 378a03ec00cSchristos userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method, 379ce11a51fSchristos const char *submethod) 380ca32bd8dSchristos { 3817a183406Schristos Authctxt *authctxt = ssh->authctxt; 382a03ec00cSchristos Authmethod *m = NULL; 383a03ec00cSchristos const char *method = packet_method; 384ca32bd8dSchristos char *methods; 385aa36fcacSchristos int r, partial = 0; 386ca32bd8dSchristos 387a03ec00cSchristos if (authenticated) { 388a03ec00cSchristos if (!authctxt->valid) { 389ca32bd8dSchristos fatal("INTERNAL ERROR: authenticated invalid user %s", 390ca32bd8dSchristos authctxt->user); 391a03ec00cSchristos } 392a03ec00cSchristos if (authctxt->postponed) 393ce11a51fSchristos fatal("INTERNAL ERROR: authenticated and postponed"); 394a03ec00cSchristos /* prefer primary authmethod name to possible synonym */ 395a03ec00cSchristos if ((m = authmethod_byname(method)) == NULL) 396a03ec00cSchristos fatal("INTERNAL ERROR: bad method %s", method); 3971c7715ddSchristos method = m->cfg->name; 398a03ec00cSchristos } 399ca32bd8dSchristos 400ca32bd8dSchristos /* Special handling for root */ 401ca32bd8dSchristos if (authenticated && authctxt->pw->pw_uid == 0 && 402ffae97bbSchristos !auth_root_allowed(ssh, method)) { 403ca32bd8dSchristos authenticated = 0; 404313c6c94Schristos #ifdef SSH_AUDIT_EVENTS 405313c6c94Schristos PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); 406313c6c94Schristos #endif 407313c6c94Schristos } 408313c6c94Schristos 409313c6c94Schristos #ifdef USE_PAM 410313c6c94Schristos if (options.use_pam && authenticated) { 4111c7715ddSchristos int success = mm_do_pam_account(); 4121c7715ddSchristos 413313c6c94Schristos /* if PAM returned a message, send it to the user */ 41455a4608bSchristos if (sshbuf_len(loginmsg) > 0) { 415aa36fcacSchristos if ((r = sshbuf_put(loginmsg, "\0", 1)) != 0) 416aa36fcacSchristos fatal("%s: buffer error: %s", 417aa36fcacSchristos __func__, ssh_err(r)); 418aa36fcacSchristos userauth_send_banner(ssh, 419aa36fcacSchristos (const char *)sshbuf_ptr(loginmsg)); 420aa36fcacSchristos if ((r = ssh_packet_write_wait(ssh)) < 0) { 421aa36fcacSchristos sshpkt_fatal(ssh, r, 422aa36fcacSchristos "%s: send PAM banner", __func__); 423aa36fcacSchristos } 424313c6c94Schristos } 4251c7715ddSchristos if (!success) { 426313c6c94Schristos fatal("Access denied for user %s by PAM account " 427313c6c94Schristos "configuration", authctxt->user); 428313c6c94Schristos } 429313c6c94Schristos } 430313c6c94Schristos #endif 431ca32bd8dSchristos 432ce11a51fSchristos if (authenticated && options.num_auth_methods != 0) { 43300a838c4Schristos if (!auth2_update_methods_lists(authctxt, method, submethod)) { 434ce11a51fSchristos authenticated = 0; 435ce11a51fSchristos partial = 1; 436ce11a51fSchristos } 437ce11a51fSchristos } 438ce11a51fSchristos 439ca32bd8dSchristos /* Log before sending the reply */ 440aa36fcacSchristos auth_log(ssh, authenticated, partial, method, submethod); 441ca32bd8dSchristos 4427a183406Schristos /* Update information exposed to session */ 4437a183406Schristos if (authenticated || partial) 4447a183406Schristos auth2_update_session_info(authctxt, method, submethod); 4457a183406Schristos 446ca32bd8dSchristos if (authctxt->postponed) 447ca32bd8dSchristos return; 448ca32bd8dSchristos 449ca32bd8dSchristos if (authenticated == 1) { 450ca32bd8dSchristos /* turn off userauth */ 451aa36fcacSchristos ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, 452aa36fcacSchristos &dispatch_protocol_ignore); 453aa36fcacSchristos if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_SUCCESS)) != 0 || 454aa36fcacSchristos (r = sshpkt_send(ssh)) != 0 || 455aa36fcacSchristos (r = ssh_packet_write_wait(ssh)) < 0) 45617418e98Schristos fatal_fr(r, "send success packet"); 457ca32bd8dSchristos /* now we can break out */ 458ca32bd8dSchristos authctxt->success = 1; 45941768fc1Schristos ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); 460ca32bd8dSchristos } else { 461ca32bd8dSchristos /* Allow initial try of "none" auth without failure penalty */ 462e4d43b82Schristos if (!partial && !authctxt->server_caused_failure && 463*79eb0734Srin (authctxt->attempt > 1 || strcmp(method, "none") != 0)) 464ca32bd8dSchristos authctxt->failures++; 4658a4530f9Schristos if (authctxt->failures >= options.max_authtries) 466aa36fcacSchristos auth_maxtries_exceeded(ssh); 467ce11a51fSchristos methods = authmethods_get(authctxt); 46817418e98Schristos debug3_f("failure partial=%d next methods=\"%s\"", 469ce11a51fSchristos partial, methods); 470aa36fcacSchristos if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_FAILURE)) != 0 || 471aa36fcacSchristos (r = sshpkt_put_cstring(ssh, methods)) != 0 || 472aa36fcacSchristos (r = sshpkt_put_u8(ssh, partial)) != 0 || 473aa36fcacSchristos (r = sshpkt_send(ssh)) != 0 || 474aa36fcacSchristos (r = ssh_packet_write_wait(ssh)) < 0) 47517418e98Schristos fatal_fr(r, "send failure packet"); 47600a838c4Schristos free(methods); 477ca32bd8dSchristos } 478ca32bd8dSchristos } 479ca32bd8dSchristos 480ce11a51fSchristos /* 481ce11a51fSchristos * Checks whether method is allowed by at least one AuthenticationMethods 482ce11a51fSchristos * methods list. Returns 1 if allowed, or no methods lists configured. 483ce11a51fSchristos * 0 otherwise. 484ce11a51fSchristos */ 48500a838c4Schristos int 48600a838c4Schristos auth2_method_allowed(Authctxt *authctxt, const char *method, 48700a838c4Schristos const char *submethod) 488ce11a51fSchristos { 489ce11a51fSchristos u_int i; 490ce11a51fSchristos 491ce11a51fSchristos /* 492ce11a51fSchristos * NB. authctxt->num_auth_methods might be zero as a result of 493ce11a51fSchristos * auth2_setup_methods_lists(), so check the configuration. 494ce11a51fSchristos */ 495ce11a51fSchristos if (options.num_auth_methods == 0) 496ce11a51fSchristos return 1; 497ce11a51fSchristos for (i = 0; i < authctxt->num_auth_methods; i++) { 49800a838c4Schristos if (list_starts_with(authctxt->auth_methods[i], method, 49900a838c4Schristos submethod) != MATCH_NONE) 500ce11a51fSchristos return 1; 501ce11a51fSchristos } 502ce11a51fSchristos return 0; 503ce11a51fSchristos } 504ce11a51fSchristos 505ca32bd8dSchristos static char * 506ce11a51fSchristos authmethods_get(Authctxt *authctxt) 507ca32bd8dSchristos { 50855a4608bSchristos struct sshbuf *b; 509ca32bd8dSchristos char *list; 51055a4608bSchristos int i, r; 511ca32bd8dSchristos 51255a4608bSchristos if ((b = sshbuf_new()) == NULL) 51317418e98Schristos fatal_f("sshbuf_new failed"); 514ca32bd8dSchristos for (i = 0; authmethods[i] != NULL; i++) { 5151c7715ddSchristos if (strcmp(authmethods[i]->cfg->name, "none") == 0) 516ca32bd8dSchristos continue; 5171c7715ddSchristos if (authmethods[i]->cfg->enabled == NULL || 5181c7715ddSchristos *(authmethods[i]->cfg->enabled) == 0) 519ce11a51fSchristos continue; 5201c7715ddSchristos if (!auth2_method_allowed(authctxt, authmethods[i]->cfg->name, 52100a838c4Schristos NULL)) 522ce11a51fSchristos continue; 52355a4608bSchristos if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "", 5241c7715ddSchristos authmethods[i]->cfg->name)) != 0) 52517418e98Schristos fatal_fr(r, "buffer error"); 526ca32bd8dSchristos } 52755a4608bSchristos if ((list = sshbuf_dup_string(b)) == NULL) 52817418e98Schristos fatal_f("sshbuf_dup_string failed"); 52955a4608bSchristos sshbuf_free(b); 530ca32bd8dSchristos return list; 531ca32bd8dSchristos } 532ca32bd8dSchristos 533ca32bd8dSchristos static Authmethod * 534a03ec00cSchristos authmethod_byname(const char *name) 535ca32bd8dSchristos { 536ca32bd8dSchristos int i; 537ca32bd8dSchristos 538a03ec00cSchristos if (name == NULL) 539a03ec00cSchristos fatal_f("NULL authentication method name"); 540a03ec00cSchristos for (i = 0; authmethods[i] != NULL; i++) { 5411c7715ddSchristos if (strcmp(name, authmethods[i]->cfg->name) == 0 || 5421c7715ddSchristos (authmethods[i]->cfg->synonym != NULL && 5431c7715ddSchristos strcmp(name, authmethods[i]->cfg->synonym) == 0)) 544ca32bd8dSchristos return authmethods[i]; 545a03ec00cSchristos } 546a03ec00cSchristos debug_f("unrecognized authentication method name: %s", name); 547ca32bd8dSchristos return NULL; 548ca32bd8dSchristos } 549ca32bd8dSchristos 550a03ec00cSchristos static Authmethod * 551a03ec00cSchristos authmethod_lookup(Authctxt *authctxt, const char *name) 552a03ec00cSchristos { 553a03ec00cSchristos Authmethod *method; 554a03ec00cSchristos 555a03ec00cSchristos if ((method = authmethod_byname(name)) == NULL) 556a03ec00cSchristos return NULL; 557a03ec00cSchristos 5581c7715ddSchristos if (method->cfg->enabled == NULL || *(method->cfg->enabled) == 0) { 559a03ec00cSchristos debug3_f("method %s not enabled", name); 560a03ec00cSchristos return NULL; 561a03ec00cSchristos } 5621c7715ddSchristos if (!auth2_method_allowed(authctxt, method->cfg->name, NULL)) { 563a03ec00cSchristos debug3_f("method %s not allowed " 564a03ec00cSchristos "by AuthenticationMethods", name); 565a03ec00cSchristos return NULL; 566a03ec00cSchristos } 567a03ec00cSchristos return method; 568a03ec00cSchristos } 569a03ec00cSchristos 570ce11a51fSchristos /* 571ce11a51fSchristos * Prune the AuthenticationMethods supplied in the configuration, removing 572ce11a51fSchristos * any methods lists that include disabled methods. Note that this might 573ce11a51fSchristos * leave authctxt->num_auth_methods == 0, even when multiple required auth 574ce11a51fSchristos * has been requested. For this reason, all tests for whether multiple is 575ce11a51fSchristos * enabled should consult options.num_auth_methods directly. 576ce11a51fSchristos */ 577ce11a51fSchristos int 578ce11a51fSchristos auth2_setup_methods_lists(Authctxt *authctxt) 579ce11a51fSchristos { 580ce11a51fSchristos u_int i; 581ce11a51fSchristos 582aa36fcacSchristos /* First, normalise away the "any" pseudo-method */ 583aa36fcacSchristos if (options.num_auth_methods == 1 && 584aa36fcacSchristos strcmp(options.auth_methods[0], "any") == 0) { 585aa36fcacSchristos free(options.auth_methods[0]); 586aa36fcacSchristos options.auth_methods[0] = NULL; 587aa36fcacSchristos options.num_auth_methods = 0; 588aa36fcacSchristos } 589aa36fcacSchristos 590ce11a51fSchristos if (options.num_auth_methods == 0) 591ce11a51fSchristos return 0; 59217418e98Schristos debug3_f("checking methods"); 593ce11a51fSchristos authctxt->auth_methods = xcalloc(options.num_auth_methods, 594ce11a51fSchristos sizeof(*authctxt->auth_methods)); 595ce11a51fSchristos authctxt->num_auth_methods = 0; 596ce11a51fSchristos for (i = 0; i < options.num_auth_methods; i++) { 597ce11a51fSchristos if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { 598ce11a51fSchristos logit("Authentication methods list \"%s\" contains " 599ce11a51fSchristos "disabled method, skipping", 600ce11a51fSchristos options.auth_methods[i]); 601ce11a51fSchristos continue; 602ce11a51fSchristos } 603ce11a51fSchristos debug("authentication methods list %d: %s", 604ce11a51fSchristos authctxt->num_auth_methods, options.auth_methods[i]); 605ce11a51fSchristos authctxt->auth_methods[authctxt->num_auth_methods++] = 606ce11a51fSchristos xstrdup(options.auth_methods[i]); 607ce11a51fSchristos } 608ce11a51fSchristos if (authctxt->num_auth_methods == 0) { 609ce11a51fSchristos error("No AuthenticationMethods left after eliminating " 610ce11a51fSchristos "disabled methods"); 611ce11a51fSchristos return -1; 612ce11a51fSchristos } 613ce11a51fSchristos return 0; 614ce11a51fSchristos } 615ce11a51fSchristos 616ce11a51fSchristos static int 61700a838c4Schristos list_starts_with(const char *methods, const char *method, 61800a838c4Schristos const char *submethod) 619ce11a51fSchristos { 620ce11a51fSchristos size_t l = strlen(method); 62100a838c4Schristos int match; 62200a838c4Schristos const char *p; 623ce11a51fSchristos 624ce11a51fSchristos if (strncmp(methods, method, l) != 0) 62500a838c4Schristos return MATCH_NONE; 62600a838c4Schristos p = methods + l; 62700a838c4Schristos match = MATCH_METHOD; 62800a838c4Schristos if (*p == ':') { 62900a838c4Schristos if (!submethod) 63000a838c4Schristos return MATCH_PARTIAL; 63100a838c4Schristos l = strlen(submethod); 63200a838c4Schristos p += 1; 63300a838c4Schristos if (strncmp(submethod, p, l)) 63400a838c4Schristos return MATCH_NONE; 63500a838c4Schristos p += l; 63600a838c4Schristos match = MATCH_BOTH; 63700a838c4Schristos } 63800a838c4Schristos if (*p != ',' && *p != '\0') 63900a838c4Schristos return MATCH_NONE; 64000a838c4Schristos return match; 641ce11a51fSchristos } 642ce11a51fSchristos 643ce11a51fSchristos /* 644ce11a51fSchristos * Remove method from the start of a comma-separated list of methods. 645ce11a51fSchristos * Returns 0 if the list of methods did not start with that method or 1 646ce11a51fSchristos * if it did. 647ce11a51fSchristos */ 648ce11a51fSchristos static int 64900a838c4Schristos remove_method(char **methods, const char *method, const char *submethod) 650ce11a51fSchristos { 65100a838c4Schristos char *omethods = *methods, *p; 652ce11a51fSchristos size_t l = strlen(method); 65300a838c4Schristos int match; 654ce11a51fSchristos 65500a838c4Schristos match = list_starts_with(omethods, method, submethod); 65600a838c4Schristos if (match != MATCH_METHOD && match != MATCH_BOTH) 657ce11a51fSchristos return 0; 65800a838c4Schristos p = omethods + l; 65900a838c4Schristos if (submethod && match == MATCH_BOTH) 66000a838c4Schristos p += 1 + strlen(submethod); /* include colon */ 66100a838c4Schristos if (*p == ',') 66200a838c4Schristos p++; 66300a838c4Schristos *methods = xstrdup(p); 664ce11a51fSchristos free(omethods); 665ce11a51fSchristos return 1; 666ce11a51fSchristos } 667ce11a51fSchristos 668ce11a51fSchristos /* 669ce11a51fSchristos * Called after successful authentication. Will remove the successful method 670ce11a51fSchristos * from the start of each list in which it occurs. If it was the last method 671ce11a51fSchristos * in any list, then authentication is deemed successful. 672ce11a51fSchristos * Returns 1 if the method completed any authentication list or 0 otherwise. 673ce11a51fSchristos */ 674ce11a51fSchristos int 67500a838c4Schristos auth2_update_methods_lists(Authctxt *authctxt, const char *method, 67600a838c4Schristos const char *submethod) 677ce11a51fSchristos { 678ce11a51fSchristos u_int i, found = 0; 679ce11a51fSchristos 68017418e98Schristos debug3_f("updating methods list after \"%s\"", method); 681ce11a51fSchristos for (i = 0; i < authctxt->num_auth_methods; i++) { 68200a838c4Schristos if (!remove_method(&(authctxt->auth_methods[i]), method, 68300a838c4Schristos submethod)) 684ce11a51fSchristos continue; 685ce11a51fSchristos found = 1; 686ce11a51fSchristos if (*authctxt->auth_methods[i] == '\0') { 687ce11a51fSchristos debug2("authentication methods list %d complete", i); 688ce11a51fSchristos return 1; 689ce11a51fSchristos } 690ce11a51fSchristos debug3("authentication methods list %d remaining: \"%s\"", 691ce11a51fSchristos i, authctxt->auth_methods[i]); 692ce11a51fSchristos } 693ce11a51fSchristos /* This should not happen, but would be bad if it did */ 694ce11a51fSchristos if (!found) 69517418e98Schristos fatal_f("method not in AuthenticationMethods"); 696ce11a51fSchristos return 0; 697ce11a51fSchristos } 698ce11a51fSchristos 6997a183406Schristos /* Reset method-specific information */ 7007a183406Schristos void auth2_authctxt_reset_info(Authctxt *authctxt) 7017a183406Schristos { 7027a183406Schristos sshkey_free(authctxt->auth_method_key); 7037a183406Schristos free(authctxt->auth_method_info); 7047a183406Schristos authctxt->auth_method_key = NULL; 7057a183406Schristos authctxt->auth_method_info = NULL; 7067a183406Schristos } 7077a183406Schristos 7087a183406Schristos /* Record auth method-specific information for logs */ 7097a183406Schristos void 7107a183406Schristos auth2_record_info(Authctxt *authctxt, const char *fmt, ...) 7117a183406Schristos { 7127a183406Schristos va_list ap; 7137a183406Schristos int i; 7147a183406Schristos 7157a183406Schristos free(authctxt->auth_method_info); 7167a183406Schristos authctxt->auth_method_info = NULL; 7177a183406Schristos 7187a183406Schristos va_start(ap, fmt); 7197a183406Schristos i = vasprintf(&authctxt->auth_method_info, fmt, ap); 7207a183406Schristos va_end(ap); 7217a183406Schristos 722cd4ada6aSchristos if (i == -1) 72317418e98Schristos fatal_f("vasprintf failed"); 7247a183406Schristos } 7257a183406Schristos 7267a183406Schristos /* 7277a183406Schristos * Records a public key used in authentication. This is used for logging 7287a183406Schristos * and to ensure that the same key is not subsequently accepted again for 7297a183406Schristos * multiple authentication. 7307a183406Schristos */ 7317a183406Schristos void 7327a183406Schristos auth2_record_key(Authctxt *authctxt, int authenticated, 7337a183406Schristos const struct sshkey *key) 7347a183406Schristos { 7357a183406Schristos struct sshkey **tmp, *dup; 7367a183406Schristos int r; 7377a183406Schristos 738aa36fcacSchristos if ((r = sshkey_from_private(key, &dup)) != 0) 73917418e98Schristos fatal_fr(r, "copy key"); 7407a183406Schristos sshkey_free(authctxt->auth_method_key); 7417a183406Schristos authctxt->auth_method_key = dup; 7427a183406Schristos 7437a183406Schristos if (!authenticated) 7447a183406Schristos return; 7457a183406Schristos 7467a183406Schristos /* If authenticated, make sure we don't accept this key again */ 747aa36fcacSchristos if ((r = sshkey_from_private(key, &dup)) != 0) 74817418e98Schristos fatal_fr(r, "copy key"); 7497a183406Schristos if (authctxt->nprev_keys >= INT_MAX || 7507a183406Schristos (tmp = recallocarray(authctxt->prev_keys, authctxt->nprev_keys, 7517a183406Schristos authctxt->nprev_keys + 1, sizeof(*authctxt->prev_keys))) == NULL) 75217418e98Schristos fatal_f("reallocarray failed"); 7537a183406Schristos authctxt->prev_keys = tmp; 7547a183406Schristos authctxt->prev_keys[authctxt->nprev_keys] = dup; 7557a183406Schristos authctxt->nprev_keys++; 7567a183406Schristos 7577a183406Schristos } 7587a183406Schristos 7597a183406Schristos /* Checks whether a key has already been previously used for authentication */ 7607a183406Schristos int 7617a183406Schristos auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) 7627a183406Schristos { 7637a183406Schristos u_int i; 7647a183406Schristos char *fp; 7657a183406Schristos 7667a183406Schristos for (i = 0; i < authctxt->nprev_keys; i++) { 7677a183406Schristos if (sshkey_equal_public(key, authctxt->prev_keys[i])) { 7687a183406Schristos fp = sshkey_fingerprint(authctxt->prev_keys[i], 7697a183406Schristos options.fingerprint_hash, SSH_FP_DEFAULT); 77017418e98Schristos debug3_f("key already used: %s %s", 7717a183406Schristos sshkey_type(authctxt->prev_keys[i]), 7727a183406Schristos fp == NULL ? "UNKNOWN" : fp); 7737a183406Schristos free(fp); 7747a183406Schristos return 1; 7757a183406Schristos } 7767a183406Schristos } 7777a183406Schristos return 0; 7787a183406Schristos } 7797a183406Schristos 7807a183406Schristos /* 7817a183406Schristos * Updates authctxt->session_info with details of authentication. Should be 7827a183406Schristos * whenever an authentication method succeeds. 7837a183406Schristos */ 7847a183406Schristos void 7857a183406Schristos auth2_update_session_info(Authctxt *authctxt, const char *method, 7867a183406Schristos const char *submethod) 7877a183406Schristos { 7887a183406Schristos int r; 7897a183406Schristos 7907a183406Schristos if (authctxt->session_info == NULL) { 7917a183406Schristos if ((authctxt->session_info = sshbuf_new()) == NULL) 79217418e98Schristos fatal_f("sshbuf_new"); 7937a183406Schristos } 7947a183406Schristos 7957a183406Schristos /* Append method[/submethod] */ 7967a183406Schristos if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s", 7977a183406Schristos method, submethod == NULL ? "" : "/", 7987a183406Schristos submethod == NULL ? "" : submethod)) != 0) 79917418e98Schristos fatal_fr(r, "append method"); 8007a183406Schristos 8017a183406Schristos /* Append key if present */ 8027a183406Schristos if (authctxt->auth_method_key != NULL) { 8037a183406Schristos if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || 8047a183406Schristos (r = sshkey_format_text(authctxt->auth_method_key, 8057a183406Schristos authctxt->session_info)) != 0) 80617418e98Schristos fatal_fr(r, "append key"); 8077a183406Schristos } 8087a183406Schristos 8097a183406Schristos if (authctxt->auth_method_info != NULL) { 8107a183406Schristos /* Ensure no ambiguity here */ 8117a183406Schristos if (strchr(authctxt->auth_method_info, '\n') != NULL) 81217418e98Schristos fatal_f("auth_method_info contains \\n"); 8137a183406Schristos if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || 8147a183406Schristos (r = sshbuf_putf(authctxt->session_info, "%s", 8157a183406Schristos authctxt->auth_method_info)) != 0) { 81617418e98Schristos fatal_fr(r, "append method info"); 8177a183406Schristos } 8187a183406Schristos } 8197a183406Schristos if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0) 82017418e98Schristos fatal_fr(r, "append"); 8217a183406Schristos } 822ce11a51fSchristos 823