1*069ac184SEd Maste /* $OpenBSD: auth2.c,v 1.168 2023/12/18 14:45:49 djm Exp $ */ 2a04a10f8SKris Kennaway /* 3a04a10f8SKris Kennaway * Copyright (c) 2000 Markus Friedl. All rights reserved. 4a04a10f8SKris Kennaway * 5a04a10f8SKris Kennaway * Redistribution and use in source and binary forms, with or without 6a04a10f8SKris Kennaway * modification, are permitted provided that the following conditions 7a04a10f8SKris Kennaway * are met: 8a04a10f8SKris Kennaway * 1. Redistributions of source code must retain the above copyright 9a04a10f8SKris Kennaway * notice, this list of conditions and the following disclaimer. 10a04a10f8SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 11a04a10f8SKris Kennaway * notice, this list of conditions and the following disclaimer in the 12a04a10f8SKris Kennaway * documentation and/or other materials provided with the distribution. 13a04a10f8SKris Kennaway * 14a04a10f8SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15a04a10f8SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16a04a10f8SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17a04a10f8SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18a04a10f8SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19a04a10f8SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20a04a10f8SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21a04a10f8SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22a04a10f8SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23a04a10f8SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24a04a10f8SKris Kennaway */ 25c2d3a559SKris Kennaway 26a04a10f8SKris Kennaway #include "includes.h" 27a04a10f8SKris Kennaway 28333ee039SDag-Erling Smørgrav #include <sys/types.h> 29d4af9e69SDag-Erling Smørgrav #include <sys/stat.h> 30d4af9e69SDag-Erling Smørgrav #include <sys/uio.h> 31333ee039SDag-Erling Smørgrav 32d4af9e69SDag-Erling Smørgrav #include <fcntl.h> 334f52dfbbSDag-Erling Smørgrav #include <limits.h> 34333ee039SDag-Erling Smørgrav #include <pwd.h> 35333ee039SDag-Erling Smørgrav #include <stdarg.h> 36333ee039SDag-Erling Smørgrav #include <string.h> 37d4af9e69SDag-Erling Smørgrav #include <unistd.h> 3819261079SEd Maste #include <time.h> 39333ee039SDag-Erling Smørgrav 4019261079SEd Maste #include "stdlib.h" 41d4af9e69SDag-Erling Smørgrav #include "atomicio.h" 427aee6ffeSDag-Erling Smørgrav #include "xmalloc.h" 43333ee039SDag-Erling Smørgrav #include "ssh2.h" 44a04a10f8SKris Kennaway #include "packet.h" 45ca3176e7SBrian Feldman #include "log.h" 46190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 47a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 48a04a10f8SKris Kennaway #include "servconf.h" 49190cef3dSDag-Erling Smørgrav #include "sshkey.h" 50333ee039SDag-Erling Smørgrav #include "hostfile.h" 51a04a10f8SKris Kennaway #include "auth.h" 52a04a10f8SKris Kennaway #include "dispatch.h" 53ca3176e7SBrian Feldman #include "pathnames.h" 54190cef3dSDag-Erling Smørgrav #include "ssherr.h" 55b2af61ecSKurt Lidl #include "blacklist_client.h" 56cf2b5f3bSDag-Erling Smørgrav #ifdef GSSAPI 57cf2b5f3bSDag-Erling Smørgrav #include "ssh-gss.h" 58cf2b5f3bSDag-Erling Smørgrav #endif 59333ee039SDag-Erling Smørgrav #include "monitor_wrap.h" 60190cef3dSDag-Erling Smørgrav #include "digest.h" 61*069ac184SEd Maste #include "kex.h" 62cf2b5f3bSDag-Erling Smørgrav 63a04a10f8SKris Kennaway /* import */ 64a04a10f8SKris Kennaway extern ServerOptions options; 65190cef3dSDag-Erling Smørgrav extern struct sshbuf *loginmsg; 66a04a10f8SKris Kennaway 6780628bacSDag-Erling Smørgrav /* methods */ 6880628bacSDag-Erling Smørgrav 6980628bacSDag-Erling Smørgrav extern Authmethod method_none; 7080628bacSDag-Erling Smørgrav extern Authmethod method_pubkey; 7180628bacSDag-Erling Smørgrav extern Authmethod method_passwd; 7280628bacSDag-Erling Smørgrav extern Authmethod method_kbdint; 7380628bacSDag-Erling Smørgrav extern Authmethod method_hostbased; 74cf2b5f3bSDag-Erling Smørgrav #ifdef GSSAPI 75cf2b5f3bSDag-Erling Smørgrav extern Authmethod method_gssapi; 76cf2b5f3bSDag-Erling Smørgrav #endif 7780628bacSDag-Erling Smørgrav 7880628bacSDag-Erling Smørgrav Authmethod *authmethods[] = { 7980628bacSDag-Erling Smørgrav &method_none, 8080628bacSDag-Erling Smørgrav &method_pubkey, 81cf2b5f3bSDag-Erling Smørgrav #ifdef GSSAPI 82cf2b5f3bSDag-Erling Smørgrav &method_gssapi, 83cf2b5f3bSDag-Erling Smørgrav #endif 8480628bacSDag-Erling Smørgrav &method_passwd, 8580628bacSDag-Erling Smørgrav &method_kbdint, 8680628bacSDag-Erling Smørgrav &method_hostbased, 8780628bacSDag-Erling Smørgrav NULL 8809958426SBrian Feldman }; 8909958426SBrian Feldman 90a04a10f8SKris Kennaway /* protocol */ 91a04a10f8SKris Kennaway 924f52dfbbSDag-Erling Smørgrav static int input_service_request(int, u_int32_t, struct ssh *); 934f52dfbbSDag-Erling Smørgrav static int input_userauth_request(int, u_int32_t, struct ssh *); 94a04a10f8SKris Kennaway 95a04a10f8SKris Kennaway /* helper */ 961323ec57SEd Maste static Authmethod *authmethod_byname(const char *); 976888a9beSDag-Erling Smørgrav static Authmethod *authmethod_lookup(Authctxt *, const char *); 986888a9beSDag-Erling Smørgrav static char *authmethods_get(Authctxt *authctxt); 99e4a9863fSDag-Erling Smørgrav 100e4a9863fSDag-Erling Smørgrav #define MATCH_NONE 0 /* method or submethod mismatch */ 101e4a9863fSDag-Erling Smørgrav #define MATCH_METHOD 1 /* method matches (no submethod specified) */ 102e4a9863fSDag-Erling Smørgrav #define MATCH_BOTH 2 /* method and submethod match */ 103e4a9863fSDag-Erling Smørgrav #define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ 104e4a9863fSDag-Erling Smørgrav static int list_starts_with(const char *, const char *, const char *); 105d4af9e69SDag-Erling Smørgrav 106d4af9e69SDag-Erling Smørgrav char * 107d4af9e69SDag-Erling Smørgrav auth2_read_banner(void) 108d4af9e69SDag-Erling Smørgrav { 109d4af9e69SDag-Erling Smørgrav struct stat st; 110d4af9e69SDag-Erling Smørgrav char *banner = NULL; 111d4af9e69SDag-Erling Smørgrav size_t len, n; 112d4af9e69SDag-Erling Smørgrav int fd; 113d4af9e69SDag-Erling Smørgrav 114d4af9e69SDag-Erling Smørgrav if ((fd = open(options.banner, O_RDONLY)) == -1) 115d4af9e69SDag-Erling Smørgrav return (NULL); 116d4af9e69SDag-Erling Smørgrav if (fstat(fd, &st) == -1) { 117d4af9e69SDag-Erling Smørgrav close(fd); 118d4af9e69SDag-Erling Smørgrav return (NULL); 119d4af9e69SDag-Erling Smørgrav } 120462c32cbSDag-Erling Smørgrav if (st.st_size <= 0 || st.st_size > 1*1024*1024) { 121d4af9e69SDag-Erling Smørgrav close(fd); 122d4af9e69SDag-Erling Smørgrav return (NULL); 123d4af9e69SDag-Erling Smørgrav } 124d4af9e69SDag-Erling Smørgrav 125d4af9e69SDag-Erling Smørgrav len = (size_t)st.st_size; /* truncate */ 126d4af9e69SDag-Erling Smørgrav banner = xmalloc(len + 1); 127d4af9e69SDag-Erling Smørgrav n = atomicio(read, fd, banner, len); 128d4af9e69SDag-Erling Smørgrav close(fd); 129d4af9e69SDag-Erling Smørgrav 130d4af9e69SDag-Erling Smørgrav if (n != len) { 131e4a9863fSDag-Erling Smørgrav free(banner); 132d4af9e69SDag-Erling Smørgrav return (NULL); 133d4af9e69SDag-Erling Smørgrav } 134d4af9e69SDag-Erling Smørgrav banner[n] = '\0'; 135d4af9e69SDag-Erling Smørgrav 136d4af9e69SDag-Erling Smørgrav return (banner); 137d4af9e69SDag-Erling Smørgrav } 138d4af9e69SDag-Erling Smørgrav 13919261079SEd Maste static void 14019261079SEd Maste userauth_send_banner(struct ssh *ssh, const char *msg) 141d4af9e69SDag-Erling Smørgrav { 14219261079SEd Maste int r; 14319261079SEd Maste 14419261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_BANNER)) != 0 || 14519261079SEd Maste (r = sshpkt_put_cstring(ssh, msg)) != 0 || 14619261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language, unused */ 14719261079SEd Maste (r = sshpkt_send(ssh)) != 0) 14819261079SEd Maste fatal_fr(r, "send packet"); 149d4af9e69SDag-Erling Smørgrav debug("%s: sent", __func__); 150d4af9e69SDag-Erling Smørgrav } 151d4af9e69SDag-Erling Smørgrav 152d4af9e69SDag-Erling Smørgrav static void 15319261079SEd Maste userauth_banner(struct ssh *ssh) 154d4af9e69SDag-Erling Smørgrav { 155d4af9e69SDag-Erling Smørgrav char *banner = NULL; 156d4af9e69SDag-Erling Smørgrav 15747dd1d1bSDag-Erling Smørgrav if (options.banner == NULL) 158d4af9e69SDag-Erling Smørgrav return; 159d4af9e69SDag-Erling Smørgrav 160d4af9e69SDag-Erling Smørgrav if ((banner = PRIVSEP(auth2_read_banner())) == NULL) 161d4af9e69SDag-Erling Smørgrav goto done; 16219261079SEd Maste userauth_send_banner(ssh, banner); 163d4af9e69SDag-Erling Smørgrav 164d4af9e69SDag-Erling Smørgrav done: 165e4a9863fSDag-Erling Smørgrav free(banner); 166d4af9e69SDag-Erling Smørgrav } 167a04a10f8SKris Kennaway 168a04a10f8SKris Kennaway /* 16909958426SBrian Feldman * loop until authctxt->success == TRUE 170a04a10f8SKris Kennaway */ 1711ec0d754SDag-Erling Smørgrav void 17219261079SEd Maste do_authentication2(struct ssh *ssh) 173a04a10f8SKris Kennaway { 17419261079SEd Maste Authctxt *authctxt = ssh->authctxt; 17519261079SEd Maste 1764f52dfbbSDag-Erling Smørgrav ssh_dispatch_init(ssh, &dispatch_protocol_error); 177*069ac184SEd Maste if (ssh->kex->ext_info_c) 178*069ac184SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info); 1794f52dfbbSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request); 1804f52dfbbSDag-Erling Smørgrav ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success); 1814f52dfbbSDag-Erling Smørgrav ssh->authctxt = NULL; 182a04a10f8SKris Kennaway } 183a04a10f8SKris Kennaway 184bc5531deSDag-Erling Smørgrav static int 1854f52dfbbSDag-Erling Smørgrav input_service_request(int type, u_int32_t seq, struct ssh *ssh) 186a04a10f8SKris Kennaway { 1874f52dfbbSDag-Erling Smørgrav Authctxt *authctxt = ssh->authctxt; 18819261079SEd Maste char *service = NULL; 18919261079SEd Maste int r, acceptit = 0; 19019261079SEd Maste 19119261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || 19219261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 19319261079SEd Maste goto out; 194a04a10f8SKris Kennaway 19509958426SBrian Feldman if (authctxt == NULL) 19609958426SBrian Feldman fatal("input_service_request: no authctxt"); 19709958426SBrian Feldman 198a04a10f8SKris Kennaway if (strcmp(service, "ssh-userauth") == 0) { 19909958426SBrian Feldman if (!authctxt->success) { 200f388f5efSDag-Erling Smørgrav acceptit = 1; 201a04a10f8SKris Kennaway /* now we can handle user-auth requests */ 20219261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, 20319261079SEd Maste &input_userauth_request); 204a04a10f8SKris Kennaway } 205a04a10f8SKris Kennaway } 206a04a10f8SKris Kennaway /* XXX all other service requests are denied */ 207a04a10f8SKris Kennaway 208f388f5efSDag-Erling Smørgrav if (acceptit) { 20919261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_ACCEPT)) != 0 || 21019261079SEd Maste (r = sshpkt_put_cstring(ssh, service)) != 0 || 21119261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 21219261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 21319261079SEd Maste goto out; 214a04a10f8SKris Kennaway } else { 215a04a10f8SKris Kennaway debug("bad service request %s", service); 21619261079SEd Maste ssh_packet_disconnect(ssh, "bad service request %s", service); 217a04a10f8SKris Kennaway } 218*069ac184SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &dispatch_protocol_error); 21919261079SEd Maste r = 0; 22019261079SEd Maste out: 221e4a9863fSDag-Erling Smørgrav free(service); 22219261079SEd Maste return r; 223a04a10f8SKris Kennaway } 224a04a10f8SKris Kennaway 225190cef3dSDag-Erling Smørgrav #define MIN_FAIL_DELAY_SECONDS 0.005 226edf85781SEd Maste #define MAX_FAIL_DELAY_SECONDS 5.0 227190cef3dSDag-Erling Smørgrav static double 228190cef3dSDag-Erling Smørgrav user_specific_delay(const char *user) 229190cef3dSDag-Erling Smørgrav { 230190cef3dSDag-Erling Smørgrav char b[512]; 231190cef3dSDag-Erling Smørgrav size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512); 232190cef3dSDag-Erling Smørgrav u_char *hash = xmalloc(len); 233190cef3dSDag-Erling Smørgrav double delay; 234190cef3dSDag-Erling Smørgrav 235190cef3dSDag-Erling Smørgrav (void)snprintf(b, sizeof b, "%llu%s", 236190cef3dSDag-Erling Smørgrav (unsigned long long)options.timing_secret, user); 237190cef3dSDag-Erling Smørgrav if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0) 23819261079SEd Maste fatal_f("ssh_digest_memory"); 239190cef3dSDag-Erling Smørgrav /* 0-4.2 ms of delay */ 240190cef3dSDag-Erling Smørgrav delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000; 241190cef3dSDag-Erling Smørgrav freezero(hash, len); 24219261079SEd Maste debug3_f("user specific delay %0.3lfms", delay/1000); 243190cef3dSDag-Erling Smørgrav return MIN_FAIL_DELAY_SECONDS + delay; 244190cef3dSDag-Erling Smørgrav } 245190cef3dSDag-Erling Smørgrav 246190cef3dSDag-Erling Smørgrav static void 247190cef3dSDag-Erling Smørgrav ensure_minimum_time_since(double start, double seconds) 248190cef3dSDag-Erling Smørgrav { 249190cef3dSDag-Erling Smørgrav struct timespec ts; 250190cef3dSDag-Erling Smørgrav double elapsed = monotime_double() - start, req = seconds, remain; 251190cef3dSDag-Erling Smørgrav 252edf85781SEd Maste if (elapsed > MAX_FAIL_DELAY_SECONDS) { 253edf85781SEd Maste debug3_f("elapsed %0.3lfms exceeded the max delay " 254edf85781SEd Maste "requested %0.3lfms)", elapsed*1000, req*1000); 255edf85781SEd Maste return; 256edf85781SEd Maste } 257edf85781SEd Maste 258190cef3dSDag-Erling Smørgrav /* if we've already passed the requested time, scale up */ 259190cef3dSDag-Erling Smørgrav while ((remain = seconds - elapsed) < 0.0) 260190cef3dSDag-Erling Smørgrav seconds *= 2; 261190cef3dSDag-Erling Smørgrav 262190cef3dSDag-Erling Smørgrav ts.tv_sec = remain; 263190cef3dSDag-Erling Smørgrav ts.tv_nsec = (remain - ts.tv_sec) * 1000000000; 26419261079SEd Maste debug3_f("elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)", 26519261079SEd Maste elapsed*1000, remain*1000, req*1000); 266190cef3dSDag-Erling Smørgrav nanosleep(&ts, NULL); 267190cef3dSDag-Erling Smørgrav } 268190cef3dSDag-Erling Smørgrav 269bc5531deSDag-Erling Smørgrav static int 2704f52dfbbSDag-Erling Smørgrav input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) 271a04a10f8SKris Kennaway { 2724f52dfbbSDag-Erling Smørgrav Authctxt *authctxt = ssh->authctxt; 27309958426SBrian Feldman Authmethod *m = NULL; 27419261079SEd Maste char *user = NULL, *service = NULL, *method = NULL, *style = NULL; 27519261079SEd Maste int r, authenticated = 0; 276190cef3dSDag-Erling Smørgrav double tstart = monotime_double(); 277d93a896eSDag-Erling Smørgrav 27809958426SBrian Feldman if (authctxt == NULL) 27909958426SBrian Feldman fatal("input_userauth_request: no authctxt"); 280a04a10f8SKris Kennaway 28119261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &user, NULL)) != 0 || 28219261079SEd Maste (r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || 28319261079SEd Maste (r = sshpkt_get_cstring(ssh, &method, NULL)) != 0) 28419261079SEd Maste goto out; 285a04a10f8SKris Kennaway debug("userauth-request for user %s service %s method %s", user, service, method); 286ca3176e7SBrian Feldman debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); 287a04a10f8SKris Kennaway 288ca3176e7SBrian Feldman if ((style = strchr(user, ':')) != NULL) 289ca3176e7SBrian Feldman *style++ = 0; 290ca3176e7SBrian Feldman 2911323ec57SEd Maste if (authctxt->attempt >= 1024) 2921323ec57SEd Maste auth_maxtries_exceeded(ssh); 293ca3176e7SBrian Feldman if (authctxt->attempt++ == 0) { 29409958426SBrian Feldman /* setup auth context */ 29519261079SEd Maste authctxt->pw = PRIVSEP(getpwnamallow(ssh, user)); 2965962c0e9SDag-Erling Smørgrav authctxt->user = xstrdup(user); 29780628bacSDag-Erling Smørgrav if (authctxt->pw && strcmp(service, "ssh-connection")==0) { 29809958426SBrian Feldman authctxt->valid = 1; 29919261079SEd Maste debug2_f("setting up authctxt for %s", user); 30009958426SBrian Feldman } else { 3011323ec57SEd Maste authctxt->valid = 0; 302d93a896eSDag-Erling Smørgrav /* Invalid user, fake password information */ 303cf2b5f3bSDag-Erling Smørgrav authctxt->pw = fakepw(); 304aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS 30519261079SEd Maste PRIVSEP(audit_event(ssh, SSH_INVALID_USER)); 306aa49c926SDag-Erling Smørgrav #endif 307a04a10f8SKris Kennaway } 308b74df5b2SDag-Erling Smørgrav #ifdef USE_PAM 309b74df5b2SDag-Erling Smørgrav if (options.use_pam) 31019261079SEd Maste PRIVSEP(start_pam(ssh)); 311b74df5b2SDag-Erling Smørgrav #endif 312d93a896eSDag-Erling Smørgrav ssh_packet_set_log_preamble(ssh, "%suser %s", 313d93a896eSDag-Erling Smørgrav authctxt->valid ? "authenticating " : "invalid ", user); 31421e764dfSDag-Erling Smørgrav setproctitle("%s%s", authctxt->valid ? user : "unknown", 31580628bacSDag-Erling Smørgrav use_privsep ? " [net]" : ""); 31609958426SBrian Feldman authctxt->service = xstrdup(service); 317af12a3e7SDag-Erling Smørgrav authctxt->style = style ? xstrdup(style) : NULL; 31880628bacSDag-Erling Smørgrav if (use_privsep) 31980628bacSDag-Erling Smørgrav mm_inform_authserv(service, style); 32019261079SEd Maste userauth_banner(ssh); 321*069ac184SEd Maste if ((r = kex_server_update_ext_info(ssh)) != 0) 322*069ac184SEd Maste fatal_fr(r, "kex_server_update_ext_info failed"); 3236888a9beSDag-Erling Smørgrav if (auth2_setup_methods_lists(authctxt) != 0) 32419261079SEd Maste ssh_packet_disconnect(ssh, 32519261079SEd Maste "no authentication methods enabled"); 326af12a3e7SDag-Erling Smørgrav } else if (strcmp(user, authctxt->user) != 0 || 32709958426SBrian Feldman strcmp(service, authctxt->service) != 0) { 32819261079SEd Maste ssh_packet_disconnect(ssh, "Change of username or service " 32919261079SEd Maste "not allowed: (%s,%s) -> (%s,%s)", 330af12a3e7SDag-Erling Smørgrav authctxt->user, authctxt->service, user, service); 331a04a10f8SKris Kennaway } 332ca3176e7SBrian Feldman /* reset state */ 3334f52dfbbSDag-Erling Smørgrav auth2_challenge_stop(ssh); 334cf2b5f3bSDag-Erling Smørgrav 335cf2b5f3bSDag-Erling Smørgrav #ifdef GSSAPI 336cce7d346SDag-Erling Smørgrav /* XXX move to auth2_gssapi_stop() */ 3374f52dfbbSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 3384f52dfbbSDag-Erling Smørgrav ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 339cf2b5f3bSDag-Erling Smørgrav #endif 340cf2b5f3bSDag-Erling Smørgrav 3414f52dfbbSDag-Erling Smørgrav auth2_authctxt_reset_info(authctxt); 342ca3176e7SBrian Feldman authctxt->postponed = 0; 343e146993eSDag-Erling Smørgrav authctxt->server_caused_failure = 0; 34403e72be8SBrian Feldman 345ca3176e7SBrian Feldman /* try to authenticate user */ 3466888a9beSDag-Erling Smørgrav m = authmethod_lookup(authctxt, method); 347d4af9e69SDag-Erling Smørgrav if (m != NULL && authctxt->failures < options.max_authtries) { 34809958426SBrian Feldman debug2("input_userauth_request: try method %s", method); 3491323ec57SEd Maste authenticated = m->userauth(ssh, method); 35009958426SBrian Feldman } 351edf85781SEd Maste if (!authctxt->authenticated && strcmp(method, "none") != 0) 352190cef3dSDag-Erling Smørgrav ensure_minimum_time_since(tstart, 353190cef3dSDag-Erling Smørgrav user_specific_delay(authctxt->user)); 3544f52dfbbSDag-Erling Smørgrav userauth_finish(ssh, authenticated, method, NULL); 35519261079SEd Maste r = 0; 35619261079SEd Maste out: 357e4a9863fSDag-Erling Smørgrav free(service); 358e4a9863fSDag-Erling Smørgrav free(user); 359e4a9863fSDag-Erling Smørgrav free(method); 36019261079SEd Maste return r; 36109958426SBrian Feldman } 36209958426SBrian Feldman 363ca3176e7SBrian Feldman void 3641323ec57SEd Maste userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method, 3656888a9beSDag-Erling Smørgrav const char *submethod) 366ca3176e7SBrian Feldman { 3674f52dfbbSDag-Erling Smørgrav Authctxt *authctxt = ssh->authctxt; 3681323ec57SEd Maste Authmethod *m = NULL; 3691323ec57SEd Maste const char *method = packet_method; 370af12a3e7SDag-Erling Smørgrav char *methods; 37119261079SEd Maste int r, partial = 0; 372af12a3e7SDag-Erling Smørgrav 3731323ec57SEd Maste if (authenticated) { 3741323ec57SEd Maste if (!authctxt->valid) { 375ca3176e7SBrian Feldman fatal("INTERNAL ERROR: authenticated invalid user %s", 376ca3176e7SBrian Feldman authctxt->user); 3771323ec57SEd Maste } 3781323ec57SEd Maste if (authctxt->postponed) 3796888a9beSDag-Erling Smørgrav fatal("INTERNAL ERROR: authenticated and postponed"); 3801323ec57SEd Maste /* prefer primary authmethod name to possible synonym */ 3811323ec57SEd Maste if ((m = authmethod_byname(method)) == NULL) 3821323ec57SEd Maste fatal("INTERNAL ERROR: bad method %s", method); 3831323ec57SEd Maste method = m->name; 3841323ec57SEd Maste } 385ca3176e7SBrian Feldman 386ca3176e7SBrian Feldman /* Special handling for root */ 387e73e9afaSDag-Erling Smørgrav if (authenticated && authctxt->pw->pw_uid == 0 && 38847dd1d1bSDag-Erling Smørgrav !auth_root_allowed(ssh, method)) { 389ca3176e7SBrian Feldman authenticated = 0; 390aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS 39119261079SEd Maste PRIVSEP(audit_event(ssh, SSH_LOGIN_ROOT_DENIED)); 392aa49c926SDag-Erling Smørgrav #endif 393aa49c926SDag-Erling Smørgrav } 394ca3176e7SBrian Feldman 3956888a9beSDag-Erling Smørgrav if (authenticated && options.num_auth_methods != 0) { 396e4a9863fSDag-Erling Smørgrav if (!auth2_update_methods_lists(authctxt, method, submethod)) { 3976888a9beSDag-Erling Smørgrav authenticated = 0; 3986888a9beSDag-Erling Smørgrav partial = 1; 3996888a9beSDag-Erling Smørgrav } 4006888a9beSDag-Erling Smørgrav } 4016888a9beSDag-Erling Smørgrav 4026888a9beSDag-Erling Smørgrav /* Log before sending the reply */ 40319261079SEd Maste auth_log(ssh, authenticated, partial, method, submethod); 4046888a9beSDag-Erling Smørgrav 4054f52dfbbSDag-Erling Smørgrav /* Update information exposed to session */ 4064f52dfbbSDag-Erling Smørgrav if (authenticated || partial) 4074f52dfbbSDag-Erling Smørgrav auth2_update_session_info(authctxt, method, submethod); 4084f52dfbbSDag-Erling Smørgrav 4096888a9beSDag-Erling Smørgrav if (authctxt->postponed) 4106888a9beSDag-Erling Smørgrav return; 4116888a9beSDag-Erling Smørgrav 412989dd127SDag-Erling Smørgrav #ifdef USE_PAM 413aa49c926SDag-Erling Smørgrav if (options.use_pam && authenticated) { 41419261079SEd Maste int r, success = PRIVSEP(do_pam_account()); 415190cef3dSDag-Erling Smørgrav 41619261079SEd Maste /* If PAM returned a message, send it to the user. */ 417190cef3dSDag-Erling Smørgrav if (sshbuf_len(loginmsg) > 0) { 418190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(loginmsg, "\0", 1)) != 0) 419190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 420190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 42119261079SEd Maste userauth_send_banner(ssh, sshbuf_ptr(loginmsg)); 42219261079SEd Maste if ((r = ssh_packet_write_wait(ssh)) != 0) { 42319261079SEd Maste sshpkt_fatal(ssh, r, 42419261079SEd Maste "%s: send PAM banner", __func__); 425aa49c926SDag-Erling Smørgrav } 42619261079SEd Maste } 42719261079SEd Maste if (!success) { 428aa49c926SDag-Erling Smørgrav fatal("Access denied for user %s by PAM account " 429aa49c926SDag-Erling Smørgrav "configuration", authctxt->user); 430aa49c926SDag-Erling Smørgrav } 431aa49c926SDag-Erling Smørgrav } 432cf2b5f3bSDag-Erling Smørgrav #endif 433989dd127SDag-Erling Smørgrav 434af12a3e7SDag-Erling Smørgrav if (authenticated == 1) { 435af12a3e7SDag-Erling Smørgrav /* turn off userauth */ 43619261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, 43719261079SEd Maste &dispatch_protocol_ignore); 43819261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_SUCCESS)) != 0 || 43919261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 44019261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 44119261079SEd Maste fatal_fr(r, "send success packet"); 442af12a3e7SDag-Erling Smørgrav /* now we can break out */ 443af12a3e7SDag-Erling Smørgrav authctxt->success = 1; 444d93a896eSDag-Erling Smørgrav ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); 445af12a3e7SDag-Erling Smørgrav } else { 446d4af9e69SDag-Erling Smørgrav /* Allow initial try of "none" auth without failure penalty */ 447bc5531deSDag-Erling Smørgrav if (!partial && !authctxt->server_caused_failure && 448342b8b88SKurt Lidl (authctxt->attempt > 1 || strcmp(method, "none") != 0)) { 449d4af9e69SDag-Erling Smørgrav authctxt->failures++; 4500f9bafdfSEd Maste BLACKLIST_NOTIFY(ssh, BLACKLIST_AUTH_FAIL, "ssh"); 451342b8b88SKurt Lidl } 452d4af9e69SDag-Erling Smørgrav if (authctxt->failures >= options.max_authtries) { 453aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS 45419261079SEd Maste PRIVSEP(audit_event(ssh, SSH_LOGIN_EXCEED_MAXTRIES)); 455aa49c926SDag-Erling Smørgrav #endif 45619261079SEd Maste auth_maxtries_exceeded(ssh); 457aa49c926SDag-Erling Smørgrav } 4586888a9beSDag-Erling Smørgrav methods = authmethods_get(authctxt); 45919261079SEd Maste debug3_f("failure partial=%d next methods=\"%s\"", 4606888a9beSDag-Erling Smørgrav partial, methods); 46119261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_FAILURE)) != 0 || 46219261079SEd Maste (r = sshpkt_put_cstring(ssh, methods)) != 0 || 46319261079SEd Maste (r = sshpkt_put_u8(ssh, partial)) != 0 || 46419261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 46519261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 46619261079SEd Maste fatal_fr(r, "send failure packet"); 467e4a9863fSDag-Erling Smørgrav free(methods); 468af12a3e7SDag-Erling Smørgrav } 469ca3176e7SBrian Feldman } 47009958426SBrian Feldman 4716888a9beSDag-Erling Smørgrav /* 4726888a9beSDag-Erling Smørgrav * Checks whether method is allowed by at least one AuthenticationMethods 4736888a9beSDag-Erling Smørgrav * methods list. Returns 1 if allowed, or no methods lists configured. 4746888a9beSDag-Erling Smørgrav * 0 otherwise. 4756888a9beSDag-Erling Smørgrav */ 476e4a9863fSDag-Erling Smørgrav int 477e4a9863fSDag-Erling Smørgrav auth2_method_allowed(Authctxt *authctxt, const char *method, 478e4a9863fSDag-Erling Smørgrav const char *submethod) 4796888a9beSDag-Erling Smørgrav { 4806888a9beSDag-Erling Smørgrav u_int i; 4816888a9beSDag-Erling Smørgrav 4826888a9beSDag-Erling Smørgrav /* 4836888a9beSDag-Erling Smørgrav * NB. authctxt->num_auth_methods might be zero as a result of 4846888a9beSDag-Erling Smørgrav * auth2_setup_methods_lists(), so check the configuration. 4856888a9beSDag-Erling Smørgrav */ 4866888a9beSDag-Erling Smørgrav if (options.num_auth_methods == 0) 4876888a9beSDag-Erling Smørgrav return 1; 4886888a9beSDag-Erling Smørgrav for (i = 0; i < authctxt->num_auth_methods; i++) { 489e4a9863fSDag-Erling Smørgrav if (list_starts_with(authctxt->auth_methods[i], method, 490e4a9863fSDag-Erling Smørgrav submethod) != MATCH_NONE) 4916888a9beSDag-Erling Smørgrav return 1; 4926888a9beSDag-Erling Smørgrav } 4936888a9beSDag-Erling Smørgrav return 0; 4946888a9beSDag-Erling Smørgrav } 4956888a9beSDag-Erling Smørgrav 496af12a3e7SDag-Erling Smørgrav static char * 4976888a9beSDag-Erling Smørgrav authmethods_get(Authctxt *authctxt) 498a04a10f8SKris Kennaway { 499190cef3dSDag-Erling Smørgrav struct sshbuf *b; 50009958426SBrian Feldman char *list; 501190cef3dSDag-Erling Smørgrav int i, r; 502a04a10f8SKris Kennaway 503190cef3dSDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) 50419261079SEd Maste fatal_f("sshbuf_new failed"); 50580628bacSDag-Erling Smørgrav for (i = 0; authmethods[i] != NULL; i++) { 50680628bacSDag-Erling Smørgrav if (strcmp(authmethods[i]->name, "none") == 0) 50709958426SBrian Feldman continue; 5086888a9beSDag-Erling Smørgrav if (authmethods[i]->enabled == NULL || 5096888a9beSDag-Erling Smørgrav *(authmethods[i]->enabled) == 0) 5106888a9beSDag-Erling Smørgrav continue; 511e4a9863fSDag-Erling Smørgrav if (!auth2_method_allowed(authctxt, authmethods[i]->name, 512e4a9863fSDag-Erling Smørgrav NULL)) 5136888a9beSDag-Erling Smørgrav continue; 514190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "", 515190cef3dSDag-Erling Smørgrav authmethods[i]->name)) != 0) 51619261079SEd Maste fatal_fr(r, "buffer error"); 51709958426SBrian Feldman } 518190cef3dSDag-Erling Smørgrav if ((list = sshbuf_dup_string(b)) == NULL) 51919261079SEd Maste fatal_f("sshbuf_dup_string failed"); 520190cef3dSDag-Erling Smørgrav sshbuf_free(b); 52109958426SBrian Feldman return list; 52209958426SBrian Feldman } 52309958426SBrian Feldman 524af12a3e7SDag-Erling Smørgrav static Authmethod * 5251323ec57SEd Maste authmethod_byname(const char *name) 52609958426SBrian Feldman { 52780628bacSDag-Erling Smørgrav int i; 52880628bacSDag-Erling Smørgrav 5291323ec57SEd Maste if (name == NULL) 5301323ec57SEd Maste fatal_f("NULL authentication method name"); 5311323ec57SEd Maste for (i = 0; authmethods[i] != NULL; i++) { 5321323ec57SEd Maste if (strcmp(name, authmethods[i]->name) == 0 || 5331323ec57SEd Maste (authmethods[i]->synonym != NULL && 5341323ec57SEd Maste strcmp(name, authmethods[i]->synonym) == 0)) 53580628bacSDag-Erling Smørgrav return authmethods[i]; 5361323ec57SEd Maste } 5371323ec57SEd Maste debug_f("unrecognized authentication method name: %s", name); 538a04a10f8SKris Kennaway return NULL; 539a04a10f8SKris Kennaway } 540d4af9e69SDag-Erling Smørgrav 5411323ec57SEd Maste static Authmethod * 5421323ec57SEd Maste authmethod_lookup(Authctxt *authctxt, const char *name) 5431323ec57SEd Maste { 5441323ec57SEd Maste Authmethod *method; 5451323ec57SEd Maste 5461323ec57SEd Maste if ((method = authmethod_byname(name)) == NULL) 5471323ec57SEd Maste return NULL; 5481323ec57SEd Maste 5491323ec57SEd Maste if (method->enabled == NULL || *(method->enabled) == 0) { 5501323ec57SEd Maste debug3_f("method %s not enabled", name); 5511323ec57SEd Maste return NULL; 5521323ec57SEd Maste } 5531323ec57SEd Maste if (!auth2_method_allowed(authctxt, method->name, NULL)) { 5541323ec57SEd Maste debug3_f("method %s not allowed " 5551323ec57SEd Maste "by AuthenticationMethods", name); 5561323ec57SEd Maste return NULL; 5571323ec57SEd Maste } 5581323ec57SEd Maste return method; 5591323ec57SEd Maste } 5601323ec57SEd Maste 5616888a9beSDag-Erling Smørgrav /* 5626888a9beSDag-Erling Smørgrav * Check a comma-separated list of methods for validity. Is need_enable is 5636888a9beSDag-Erling Smørgrav * non-zero, then also require that the methods are enabled. 5646888a9beSDag-Erling Smørgrav * Returns 0 on success or -1 if the methods list is invalid. 5656888a9beSDag-Erling Smørgrav */ 5666888a9beSDag-Erling Smørgrav int 5676888a9beSDag-Erling Smørgrav auth2_methods_valid(const char *_methods, int need_enable) 5686888a9beSDag-Erling Smørgrav { 569e4a9863fSDag-Erling Smørgrav char *methods, *omethods, *method, *p; 5706888a9beSDag-Erling Smørgrav u_int i, found; 5716888a9beSDag-Erling Smørgrav int ret = -1; 5726888a9beSDag-Erling Smørgrav 5736888a9beSDag-Erling Smørgrav if (*_methods == '\0') { 5746888a9beSDag-Erling Smørgrav error("empty authentication method list"); 5756888a9beSDag-Erling Smørgrav return -1; 5766888a9beSDag-Erling Smørgrav } 5776888a9beSDag-Erling Smørgrav omethods = methods = xstrdup(_methods); 5786888a9beSDag-Erling Smørgrav while ((method = strsep(&methods, ",")) != NULL) { 5796888a9beSDag-Erling Smørgrav for (found = i = 0; !found && authmethods[i] != NULL; i++) { 580e4a9863fSDag-Erling Smørgrav if ((p = strchr(method, ':')) != NULL) 581e4a9863fSDag-Erling Smørgrav *p = '\0'; 5826888a9beSDag-Erling Smørgrav if (strcmp(method, authmethods[i]->name) != 0) 5836888a9beSDag-Erling Smørgrav continue; 5846888a9beSDag-Erling Smørgrav if (need_enable) { 5856888a9beSDag-Erling Smørgrav if (authmethods[i]->enabled == NULL || 5866888a9beSDag-Erling Smørgrav *(authmethods[i]->enabled) == 0) { 5876888a9beSDag-Erling Smørgrav error("Disabled method \"%s\" in " 5886888a9beSDag-Erling Smørgrav "AuthenticationMethods list \"%s\"", 5896888a9beSDag-Erling Smørgrav method, _methods); 5906888a9beSDag-Erling Smørgrav goto out; 5916888a9beSDag-Erling Smørgrav } 5926888a9beSDag-Erling Smørgrav } 5936888a9beSDag-Erling Smørgrav found = 1; 5946888a9beSDag-Erling Smørgrav break; 5956888a9beSDag-Erling Smørgrav } 5966888a9beSDag-Erling Smørgrav if (!found) { 5976888a9beSDag-Erling Smørgrav error("Unknown authentication method \"%s\" in list", 5986888a9beSDag-Erling Smørgrav method); 5996888a9beSDag-Erling Smørgrav goto out; 6006888a9beSDag-Erling Smørgrav } 6016888a9beSDag-Erling Smørgrav } 6026888a9beSDag-Erling Smørgrav ret = 0; 6036888a9beSDag-Erling Smørgrav out: 6046888a9beSDag-Erling Smørgrav free(omethods); 6056888a9beSDag-Erling Smørgrav return ret; 6066888a9beSDag-Erling Smørgrav } 6076888a9beSDag-Erling Smørgrav 6086888a9beSDag-Erling Smørgrav /* 6096888a9beSDag-Erling Smørgrav * Prune the AuthenticationMethods supplied in the configuration, removing 6106888a9beSDag-Erling Smørgrav * any methods lists that include disabled methods. Note that this might 6116888a9beSDag-Erling Smørgrav * leave authctxt->num_auth_methods == 0, even when multiple required auth 6126888a9beSDag-Erling Smørgrav * has been requested. For this reason, all tests for whether multiple is 6136888a9beSDag-Erling Smørgrav * enabled should consult options.num_auth_methods directly. 6146888a9beSDag-Erling Smørgrav */ 6156888a9beSDag-Erling Smørgrav int 6166888a9beSDag-Erling Smørgrav auth2_setup_methods_lists(Authctxt *authctxt) 6176888a9beSDag-Erling Smørgrav { 6186888a9beSDag-Erling Smørgrav u_int i; 6196888a9beSDag-Erling Smørgrav 62019261079SEd Maste /* First, normalise away the "any" pseudo-method */ 62119261079SEd Maste if (options.num_auth_methods == 1 && 62219261079SEd Maste strcmp(options.auth_methods[0], "any") == 0) { 62319261079SEd Maste free(options.auth_methods[0]); 62419261079SEd Maste options.auth_methods[0] = NULL; 62519261079SEd Maste options.num_auth_methods = 0; 62619261079SEd Maste } 62719261079SEd Maste 6286888a9beSDag-Erling Smørgrav if (options.num_auth_methods == 0) 6296888a9beSDag-Erling Smørgrav return 0; 63019261079SEd Maste debug3_f("checking methods"); 6316888a9beSDag-Erling Smørgrav authctxt->auth_methods = xcalloc(options.num_auth_methods, 6326888a9beSDag-Erling Smørgrav sizeof(*authctxt->auth_methods)); 6336888a9beSDag-Erling Smørgrav authctxt->num_auth_methods = 0; 6346888a9beSDag-Erling Smørgrav for (i = 0; i < options.num_auth_methods; i++) { 6356888a9beSDag-Erling Smørgrav if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { 6366888a9beSDag-Erling Smørgrav logit("Authentication methods list \"%s\" contains " 6376888a9beSDag-Erling Smørgrav "disabled method, skipping", 6386888a9beSDag-Erling Smørgrav options.auth_methods[i]); 6396888a9beSDag-Erling Smørgrav continue; 6406888a9beSDag-Erling Smørgrav } 6416888a9beSDag-Erling Smørgrav debug("authentication methods list %d: %s", 6426888a9beSDag-Erling Smørgrav authctxt->num_auth_methods, options.auth_methods[i]); 6436888a9beSDag-Erling Smørgrav authctxt->auth_methods[authctxt->num_auth_methods++] = 6446888a9beSDag-Erling Smørgrav xstrdup(options.auth_methods[i]); 6456888a9beSDag-Erling Smørgrav } 6466888a9beSDag-Erling Smørgrav if (authctxt->num_auth_methods == 0) { 6476888a9beSDag-Erling Smørgrav error("No AuthenticationMethods left after eliminating " 6486888a9beSDag-Erling Smørgrav "disabled methods"); 6496888a9beSDag-Erling Smørgrav return -1; 6506888a9beSDag-Erling Smørgrav } 6516888a9beSDag-Erling Smørgrav return 0; 6526888a9beSDag-Erling Smørgrav } 6536888a9beSDag-Erling Smørgrav 6546888a9beSDag-Erling Smørgrav static int 655e4a9863fSDag-Erling Smørgrav list_starts_with(const char *methods, const char *method, 656e4a9863fSDag-Erling Smørgrav const char *submethod) 6576888a9beSDag-Erling Smørgrav { 6586888a9beSDag-Erling Smørgrav size_t l = strlen(method); 659e4a9863fSDag-Erling Smørgrav int match; 660e4a9863fSDag-Erling Smørgrav const char *p; 6616888a9beSDag-Erling Smørgrav 6626888a9beSDag-Erling Smørgrav if (strncmp(methods, method, l) != 0) 663e4a9863fSDag-Erling Smørgrav return MATCH_NONE; 664e4a9863fSDag-Erling Smørgrav p = methods + l; 665e4a9863fSDag-Erling Smørgrav match = MATCH_METHOD; 666e4a9863fSDag-Erling Smørgrav if (*p == ':') { 667e4a9863fSDag-Erling Smørgrav if (!submethod) 668e4a9863fSDag-Erling Smørgrav return MATCH_PARTIAL; 669e4a9863fSDag-Erling Smørgrav l = strlen(submethod); 670e4a9863fSDag-Erling Smørgrav p += 1; 671e4a9863fSDag-Erling Smørgrav if (strncmp(submethod, p, l)) 672e4a9863fSDag-Erling Smørgrav return MATCH_NONE; 673e4a9863fSDag-Erling Smørgrav p += l; 674e4a9863fSDag-Erling Smørgrav match = MATCH_BOTH; 675e4a9863fSDag-Erling Smørgrav } 676e4a9863fSDag-Erling Smørgrav if (*p != ',' && *p != '\0') 677e4a9863fSDag-Erling Smørgrav return MATCH_NONE; 678e4a9863fSDag-Erling Smørgrav return match; 6796888a9beSDag-Erling Smørgrav } 6806888a9beSDag-Erling Smørgrav 6816888a9beSDag-Erling Smørgrav /* 6826888a9beSDag-Erling Smørgrav * Remove method from the start of a comma-separated list of methods. 6836888a9beSDag-Erling Smørgrav * Returns 0 if the list of methods did not start with that method or 1 6846888a9beSDag-Erling Smørgrav * if it did. 6856888a9beSDag-Erling Smørgrav */ 6866888a9beSDag-Erling Smørgrav static int 687e4a9863fSDag-Erling Smørgrav remove_method(char **methods, const char *method, const char *submethod) 6886888a9beSDag-Erling Smørgrav { 689e4a9863fSDag-Erling Smørgrav char *omethods = *methods, *p; 6906888a9beSDag-Erling Smørgrav size_t l = strlen(method); 691e4a9863fSDag-Erling Smørgrav int match; 6926888a9beSDag-Erling Smørgrav 693e4a9863fSDag-Erling Smørgrav match = list_starts_with(omethods, method, submethod); 694e4a9863fSDag-Erling Smørgrav if (match != MATCH_METHOD && match != MATCH_BOTH) 6956888a9beSDag-Erling Smørgrav return 0; 696e4a9863fSDag-Erling Smørgrav p = omethods + l; 697e4a9863fSDag-Erling Smørgrav if (submethod && match == MATCH_BOTH) 698e4a9863fSDag-Erling Smørgrav p += 1 + strlen(submethod); /* include colon */ 699e4a9863fSDag-Erling Smørgrav if (*p == ',') 700e4a9863fSDag-Erling Smørgrav p++; 701e4a9863fSDag-Erling Smørgrav *methods = xstrdup(p); 7026888a9beSDag-Erling Smørgrav free(omethods); 7036888a9beSDag-Erling Smørgrav return 1; 7046888a9beSDag-Erling Smørgrav } 7056888a9beSDag-Erling Smørgrav 7066888a9beSDag-Erling Smørgrav /* 7076888a9beSDag-Erling Smørgrav * Called after successful authentication. Will remove the successful method 7086888a9beSDag-Erling Smørgrav * from the start of each list in which it occurs. If it was the last method 7096888a9beSDag-Erling Smørgrav * in any list, then authentication is deemed successful. 7106888a9beSDag-Erling Smørgrav * Returns 1 if the method completed any authentication list or 0 otherwise. 7116888a9beSDag-Erling Smørgrav */ 7126888a9beSDag-Erling Smørgrav int 713e4a9863fSDag-Erling Smørgrav auth2_update_methods_lists(Authctxt *authctxt, const char *method, 714e4a9863fSDag-Erling Smørgrav const char *submethod) 7156888a9beSDag-Erling Smørgrav { 7166888a9beSDag-Erling Smørgrav u_int i, found = 0; 7176888a9beSDag-Erling Smørgrav 71819261079SEd Maste debug3_f("updating methods list after \"%s\"", method); 7196888a9beSDag-Erling Smørgrav for (i = 0; i < authctxt->num_auth_methods; i++) { 720e4a9863fSDag-Erling Smørgrav if (!remove_method(&(authctxt->auth_methods[i]), method, 721e4a9863fSDag-Erling Smørgrav submethod)) 7226888a9beSDag-Erling Smørgrav continue; 7236888a9beSDag-Erling Smørgrav found = 1; 7246888a9beSDag-Erling Smørgrav if (*authctxt->auth_methods[i] == '\0') { 7256888a9beSDag-Erling Smørgrav debug2("authentication methods list %d complete", i); 7266888a9beSDag-Erling Smørgrav return 1; 7276888a9beSDag-Erling Smørgrav } 7286888a9beSDag-Erling Smørgrav debug3("authentication methods list %d remaining: \"%s\"", 7296888a9beSDag-Erling Smørgrav i, authctxt->auth_methods[i]); 7306888a9beSDag-Erling Smørgrav } 7316888a9beSDag-Erling Smørgrav /* This should not happen, but would be bad if it did */ 7326888a9beSDag-Erling Smørgrav if (!found) 73319261079SEd Maste fatal_f("method not in AuthenticationMethods"); 7346888a9beSDag-Erling Smørgrav return 0; 7356888a9beSDag-Erling Smørgrav } 7366888a9beSDag-Erling Smørgrav 7374f52dfbbSDag-Erling Smørgrav /* Reset method-specific information */ 7384f52dfbbSDag-Erling Smørgrav void auth2_authctxt_reset_info(Authctxt *authctxt) 7394f52dfbbSDag-Erling Smørgrav { 7404f52dfbbSDag-Erling Smørgrav sshkey_free(authctxt->auth_method_key); 7414f52dfbbSDag-Erling Smørgrav free(authctxt->auth_method_info); 7424f52dfbbSDag-Erling Smørgrav authctxt->auth_method_key = NULL; 7434f52dfbbSDag-Erling Smørgrav authctxt->auth_method_info = NULL; 7444f52dfbbSDag-Erling Smørgrav } 7454f52dfbbSDag-Erling Smørgrav 7464f52dfbbSDag-Erling Smørgrav /* Record auth method-specific information for logs */ 7474f52dfbbSDag-Erling Smørgrav void 7484f52dfbbSDag-Erling Smørgrav auth2_record_info(Authctxt *authctxt, const char *fmt, ...) 7494f52dfbbSDag-Erling Smørgrav { 7504f52dfbbSDag-Erling Smørgrav va_list ap; 7514f52dfbbSDag-Erling Smørgrav int i; 7524f52dfbbSDag-Erling Smørgrav 7534f52dfbbSDag-Erling Smørgrav free(authctxt->auth_method_info); 7544f52dfbbSDag-Erling Smørgrav authctxt->auth_method_info = NULL; 7554f52dfbbSDag-Erling Smørgrav 7564f52dfbbSDag-Erling Smørgrav va_start(ap, fmt); 7574f52dfbbSDag-Erling Smørgrav i = vasprintf(&authctxt->auth_method_info, fmt, ap); 7584f52dfbbSDag-Erling Smørgrav va_end(ap); 7594f52dfbbSDag-Erling Smørgrav 76019261079SEd Maste if (i == -1) 76119261079SEd Maste fatal_f("vasprintf failed"); 7624f52dfbbSDag-Erling Smørgrav } 7634f52dfbbSDag-Erling Smørgrav 7644f52dfbbSDag-Erling Smørgrav /* 7654f52dfbbSDag-Erling Smørgrav * Records a public key used in authentication. This is used for logging 7664f52dfbbSDag-Erling Smørgrav * and to ensure that the same key is not subsequently accepted again for 7674f52dfbbSDag-Erling Smørgrav * multiple authentication. 7684f52dfbbSDag-Erling Smørgrav */ 7694f52dfbbSDag-Erling Smørgrav void 7704f52dfbbSDag-Erling Smørgrav auth2_record_key(Authctxt *authctxt, int authenticated, 7714f52dfbbSDag-Erling Smørgrav const struct sshkey *key) 7724f52dfbbSDag-Erling Smørgrav { 7734f52dfbbSDag-Erling Smørgrav struct sshkey **tmp, *dup; 7744f52dfbbSDag-Erling Smørgrav int r; 7754f52dfbbSDag-Erling Smørgrav 7762a01feabSEd Maste if ((r = sshkey_from_private(key, &dup)) != 0) 77719261079SEd Maste fatal_fr(r, "copy key"); 7784f52dfbbSDag-Erling Smørgrav sshkey_free(authctxt->auth_method_key); 7794f52dfbbSDag-Erling Smørgrav authctxt->auth_method_key = dup; 7804f52dfbbSDag-Erling Smørgrav 7814f52dfbbSDag-Erling Smørgrav if (!authenticated) 7824f52dfbbSDag-Erling Smørgrav return; 7834f52dfbbSDag-Erling Smørgrav 7844f52dfbbSDag-Erling Smørgrav /* If authenticated, make sure we don't accept this key again */ 7852a01feabSEd Maste if ((r = sshkey_from_private(key, &dup)) != 0) 78619261079SEd Maste fatal_fr(r, "copy key"); 7874f52dfbbSDag-Erling Smørgrav if (authctxt->nprev_keys >= INT_MAX || 7884f52dfbbSDag-Erling Smørgrav (tmp = recallocarray(authctxt->prev_keys, authctxt->nprev_keys, 7894f52dfbbSDag-Erling Smørgrav authctxt->nprev_keys + 1, sizeof(*authctxt->prev_keys))) == NULL) 79019261079SEd Maste fatal_f("reallocarray failed"); 7914f52dfbbSDag-Erling Smørgrav authctxt->prev_keys = tmp; 7924f52dfbbSDag-Erling Smørgrav authctxt->prev_keys[authctxt->nprev_keys] = dup; 7934f52dfbbSDag-Erling Smørgrav authctxt->nprev_keys++; 7944f52dfbbSDag-Erling Smørgrav 7954f52dfbbSDag-Erling Smørgrav } 7964f52dfbbSDag-Erling Smørgrav 7974f52dfbbSDag-Erling Smørgrav /* Checks whether a key has already been previously used for authentication */ 7984f52dfbbSDag-Erling Smørgrav int 7994f52dfbbSDag-Erling Smørgrav auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) 8004f52dfbbSDag-Erling Smørgrav { 8014f52dfbbSDag-Erling Smørgrav u_int i; 8024f52dfbbSDag-Erling Smørgrav char *fp; 8034f52dfbbSDag-Erling Smørgrav 8044f52dfbbSDag-Erling Smørgrav for (i = 0; i < authctxt->nprev_keys; i++) { 8054f52dfbbSDag-Erling Smørgrav if (sshkey_equal_public(key, authctxt->prev_keys[i])) { 8064f52dfbbSDag-Erling Smørgrav fp = sshkey_fingerprint(authctxt->prev_keys[i], 8074f52dfbbSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 80819261079SEd Maste debug3_f("key already used: %s %s", 8094f52dfbbSDag-Erling Smørgrav sshkey_type(authctxt->prev_keys[i]), 8104f52dfbbSDag-Erling Smørgrav fp == NULL ? "UNKNOWN" : fp); 8114f52dfbbSDag-Erling Smørgrav free(fp); 8124f52dfbbSDag-Erling Smørgrav return 1; 8134f52dfbbSDag-Erling Smørgrav } 8144f52dfbbSDag-Erling Smørgrav } 8154f52dfbbSDag-Erling Smørgrav return 0; 8164f52dfbbSDag-Erling Smørgrav } 8174f52dfbbSDag-Erling Smørgrav 8184f52dfbbSDag-Erling Smørgrav /* 8194f52dfbbSDag-Erling Smørgrav * Updates authctxt->session_info with details of authentication. Should be 8204f52dfbbSDag-Erling Smørgrav * whenever an authentication method succeeds. 8214f52dfbbSDag-Erling Smørgrav */ 8224f52dfbbSDag-Erling Smørgrav void 8234f52dfbbSDag-Erling Smørgrav auth2_update_session_info(Authctxt *authctxt, const char *method, 8244f52dfbbSDag-Erling Smørgrav const char *submethod) 8254f52dfbbSDag-Erling Smørgrav { 8264f52dfbbSDag-Erling Smørgrav int r; 8274f52dfbbSDag-Erling Smørgrav 8284f52dfbbSDag-Erling Smørgrav if (authctxt->session_info == NULL) { 8294f52dfbbSDag-Erling Smørgrav if ((authctxt->session_info = sshbuf_new()) == NULL) 83019261079SEd Maste fatal_f("sshbuf_new"); 8314f52dfbbSDag-Erling Smørgrav } 8324f52dfbbSDag-Erling Smørgrav 8334f52dfbbSDag-Erling Smørgrav /* Append method[/submethod] */ 8344f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s", 8354f52dfbbSDag-Erling Smørgrav method, submethod == NULL ? "" : "/", 8364f52dfbbSDag-Erling Smørgrav submethod == NULL ? "" : submethod)) != 0) 83719261079SEd Maste fatal_fr(r, "append method"); 8384f52dfbbSDag-Erling Smørgrav 8394f52dfbbSDag-Erling Smørgrav /* Append key if present */ 8404f52dfbbSDag-Erling Smørgrav if (authctxt->auth_method_key != NULL) { 8414f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || 8424f52dfbbSDag-Erling Smørgrav (r = sshkey_format_text(authctxt->auth_method_key, 8434f52dfbbSDag-Erling Smørgrav authctxt->session_info)) != 0) 84419261079SEd Maste fatal_fr(r, "append key"); 8454f52dfbbSDag-Erling Smørgrav } 8464f52dfbbSDag-Erling Smørgrav 8474f52dfbbSDag-Erling Smørgrav if (authctxt->auth_method_info != NULL) { 8484f52dfbbSDag-Erling Smørgrav /* Ensure no ambiguity here */ 8494f52dfbbSDag-Erling Smørgrav if (strchr(authctxt->auth_method_info, '\n') != NULL) 85019261079SEd Maste fatal_f("auth_method_info contains \\n"); 8514f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || 8524f52dfbbSDag-Erling Smørgrav (r = sshbuf_putf(authctxt->session_info, "%s", 8534f52dfbbSDag-Erling Smørgrav authctxt->auth_method_info)) != 0) { 85419261079SEd Maste fatal_fr(r, "append method info"); 8554f52dfbbSDag-Erling Smørgrav } 8564f52dfbbSDag-Erling Smørgrav } 8574f52dfbbSDag-Erling Smørgrav if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0) 85819261079SEd Maste fatal_fr(r, "append"); 8594f52dfbbSDag-Erling Smørgrav } 8606888a9beSDag-Erling Smørgrav 861