auth2.c revision 248619
1248619Sdes/* $OpenBSD: auth2.c,v 1.126 2012/12/02 20:34:09 djm Exp $ */ 260573Skris/* 360573Skris * Copyright (c) 2000 Markus Friedl. All rights reserved. 460573Skris * 560573Skris * Redistribution and use in source and binary forms, with or without 660573Skris * modification, are permitted provided that the following conditions 760573Skris * are met: 860573Skris * 1. Redistributions of source code must retain the above copyright 960573Skris * notice, this list of conditions and the following disclaimer. 1060573Skris * 2. Redistributions in binary form must reproduce the above copyright 1160573Skris * notice, this list of conditions and the following disclaimer in the 1260573Skris * documentation and/or other materials provided with the distribution. 1360573Skris * 1460573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1560573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1660573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1760573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1860573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1960573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2060573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2160573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2260573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2360573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2460573Skris */ 2565674Skris 2660573Skris#include "includes.h" 27162856Sdes__RCSID("$FreeBSD: head/crypto/openssh/auth2.c 248619 2013-03-22 17:55:38Z des $"); 2860573Skris 29162856Sdes#include <sys/types.h> 30181111Sdes#include <sys/stat.h> 31181111Sdes#include <sys/uio.h> 32162856Sdes 33181111Sdes#include <fcntl.h> 34162856Sdes#include <pwd.h> 35162856Sdes#include <stdarg.h> 36162856Sdes#include <string.h> 37181111Sdes#include <unistd.h> 38162856Sdes 39197679Sdes#include "atomicio.h" 40162856Sdes#include "xmalloc.h" 4176262Sgreen#include "ssh2.h" 4260573Skris#include "packet.h" 4376262Sgreen#include "log.h" 44162856Sdes#include "buffer.h" 4560573Skris#include "servconf.h" 4660573Skris#include "compat.h" 47162856Sdes#include "key.h" 48162856Sdes#include "hostfile.h" 4960573Skris#include "auth.h" 5060573Skris#include "dispatch.h" 5176262Sgreen#include "pathnames.h" 52147005Sdes#include "buffer.h" 53162856Sdes#include "canohost.h" 5460573Skris 55124211Sdes#ifdef GSSAPI 56124211Sdes#include "ssh-gss.h" 57124211Sdes#endif 58162856Sdes#include "monitor_wrap.h" 59124211Sdes 6060573Skris/* import */ 6160573Skrisextern ServerOptions options; 6276262Sgreenextern u_char *session_id2; 63124211Sdesextern u_int session_id2_len; 64147005Sdesextern Buffer loginmsg; 6560573Skris 6698684Sdes/* methods */ 6798684Sdes 6898684Sdesextern Authmethod method_none; 6998684Sdesextern Authmethod method_pubkey; 7098684Sdesextern Authmethod method_passwd; 7198684Sdesextern Authmethod method_kbdint; 7298684Sdesextern Authmethod method_hostbased; 73124211Sdes#ifdef GSSAPI 74124211Sdesextern Authmethod method_gssapi; 75124211Sdes#endif 76192595Sdes#ifdef JPAKE 77192595Sdesextern Authmethod method_jpake; 78192595Sdes#endif 7998684Sdes 8098684SdesAuthmethod *authmethods[] = { 8198684Sdes &method_none, 8298684Sdes &method_pubkey, 83124211Sdes#ifdef GSSAPI 84124211Sdes &method_gssapi, 85124211Sdes#endif 86192595Sdes#ifdef JPAKE 87192595Sdes &method_jpake, 88192595Sdes#endif 8998684Sdes &method_passwd, 9098684Sdes &method_kbdint, 9198684Sdes &method_hostbased, 9298684Sdes NULL 9369591Sgreen}; 9469591Sgreen 9560573Skris/* protocol */ 9660573Skris 9792559Sdesstatic void input_service_request(int, u_int32_t, void *); 9892559Sdesstatic void input_userauth_request(int, u_int32_t, void *); 9960573Skris 10060573Skris/* helper */ 101248619Sdesstatic Authmethod *authmethod_lookup(Authctxt *, const char *); 102248619Sdesstatic char *authmethods_get(Authctxt *authctxt); 103248619Sdesstatic int method_allowed(Authctxt *, const char *); 104248619Sdesstatic int list_starts_with(const char *, const char *); 10560573Skris 106181111Sdeschar * 107181111Sdesauth2_read_banner(void) 108181111Sdes{ 109181111Sdes struct stat st; 110181111Sdes char *banner = NULL; 111181111Sdes size_t len, n; 112181111Sdes int fd; 113181111Sdes 114181111Sdes if ((fd = open(options.banner, O_RDONLY)) == -1) 115181111Sdes return (NULL); 116181111Sdes if (fstat(fd, &st) == -1) { 117181111Sdes close(fd); 118181111Sdes return (NULL); 119181111Sdes } 120240075Sdes if (st.st_size <= 0 || st.st_size > 1*1024*1024) { 121181111Sdes close(fd); 122181111Sdes return (NULL); 123181111Sdes } 124181111Sdes 125181111Sdes len = (size_t)st.st_size; /* truncate */ 126181111Sdes banner = xmalloc(len + 1); 127181111Sdes n = atomicio(read, fd, banner, len); 128181111Sdes close(fd); 129181111Sdes 130181111Sdes if (n != len) { 131181111Sdes xfree(banner); 132181111Sdes return (NULL); 133181111Sdes } 134181111Sdes banner[n] = '\0'; 135181111Sdes 136181111Sdes return (banner); 137181111Sdes} 138181111Sdes 139181111Sdesvoid 140181111Sdesuserauth_send_banner(const char *msg) 141181111Sdes{ 142181111Sdes if (datafellows & SSH_BUG_BANNER) 143181111Sdes return; 144181111Sdes 145181111Sdes packet_start(SSH2_MSG_USERAUTH_BANNER); 146181111Sdes packet_put_cstring(msg); 147181111Sdes packet_put_cstring(""); /* language, unused */ 148181111Sdes packet_send(); 149181111Sdes debug("%s: sent", __func__); 150181111Sdes} 151181111Sdes 152181111Sdesstatic void 153181111Sdesuserauth_banner(void) 154181111Sdes{ 155181111Sdes char *banner = NULL; 156181111Sdes 157181111Sdes if (options.banner == NULL || 158181111Sdes strcasecmp(options.banner, "none") == 0 || 159181111Sdes (datafellows & SSH_BUG_BANNER) != 0) 160181111Sdes return; 161181111Sdes 162181111Sdes if ((banner = PRIVSEP(auth2_read_banner())) == NULL) 163181111Sdes goto done; 164181111Sdes userauth_send_banner(banner); 165181111Sdes 166181111Sdesdone: 167181111Sdes if (banner) 168181111Sdes xfree(banner); 169181111Sdes} 170181111Sdes 17160573Skris/* 17269591Sgreen * loop until authctxt->success == TRUE 17360573Skris */ 174126277Sdesvoid 175126277Sdesdo_authentication2(Authctxt *authctxt) 17660573Skris{ 17792559Sdes dispatch_init(&dispatch_protocol_error); 17860573Skris dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); 17969591Sgreen dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); 18060573Skris} 18160573Skris 182162856Sdes/*ARGSUSED*/ 18392559Sdesstatic void 18492559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt) 18560573Skris{ 18669591Sgreen Authctxt *authctxt = ctxt; 18776262Sgreen u_int len; 188106130Sdes int acceptit = 0; 189221420Sdes char *service = packet_get_cstring(&len); 19092559Sdes packet_check_eom(); 19160573Skris 19269591Sgreen if (authctxt == NULL) 19369591Sgreen fatal("input_service_request: no authctxt"); 19469591Sgreen 19560573Skris if (strcmp(service, "ssh-userauth") == 0) { 19669591Sgreen if (!authctxt->success) { 197106130Sdes acceptit = 1; 19860573Skris /* now we can handle user-auth requests */ 19960573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); 20060573Skris } 20160573Skris } 20260573Skris /* XXX all other service requests are denied */ 20360573Skris 204106130Sdes if (acceptit) { 20560573Skris packet_start(SSH2_MSG_SERVICE_ACCEPT); 20660573Skris packet_put_cstring(service); 20760573Skris packet_send(); 20860573Skris packet_write_wait(); 20960573Skris } else { 21060573Skris debug("bad service request %s", service); 21160573Skris packet_disconnect("bad service request %s", service); 21260573Skris } 21360573Skris xfree(service); 21460573Skris} 21560573Skris 216162856Sdes/*ARGSUSED*/ 21792559Sdesstatic void 21892559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt) 21960573Skris{ 22069591Sgreen Authctxt *authctxt = ctxt; 22169591Sgreen Authmethod *m = NULL; 22276262Sgreen char *user, *service, *method, *style = NULL; 22360573Skris int authenticated = 0; 22499053Sdes#ifdef HAVE_LOGIN_CAP 22599053Sdes login_cap_t *lc; 22699053Sdes const char *from_host, *from_ip; 22760573Skris 228231584Sed from_host = get_canonical_hostname(options.use_dns); 229231584Sed from_ip = get_remote_ipaddr(); 23099053Sdes#endif 23199053Sdes 23269591Sgreen if (authctxt == NULL) 23369591Sgreen fatal("input_userauth_request: no authctxt"); 23460573Skris 235221420Sdes user = packet_get_cstring(NULL); 236221420Sdes service = packet_get_cstring(NULL); 237221420Sdes method = packet_get_cstring(NULL); 23860573Skris debug("userauth-request for user %s service %s method %s", user, service, method); 23976262Sgreen debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); 24060573Skris 24176262Sgreen if ((style = strchr(user, ':')) != NULL) 24276262Sgreen *style++ = 0; 24376262Sgreen 24476262Sgreen if (authctxt->attempt++ == 0) { 24569591Sgreen /* setup auth context */ 24698684Sdes authctxt->pw = PRIVSEP(getpwnamallow(user)); 247128460Sdes authctxt->user = xstrdup(user); 24898684Sdes if (authctxt->pw && strcmp(service, "ssh-connection")==0) { 24969591Sgreen authctxt->valid = 1; 25069591Sgreen debug2("input_userauth_request: setting up authctxt for %s", user); 25169591Sgreen } else { 252137019Sdes logit("input_userauth_request: invalid user %s", user); 253124211Sdes authctxt->pw = fakepw(); 254147005Sdes#ifdef SSH_AUDIT_EVENTS 255147005Sdes PRIVSEP(audit_event(SSH_INVALID_USER)); 256147005Sdes#endif 25760573Skris } 258157019Sdes#ifdef USE_PAM 259157019Sdes if (options.use_pam) 260157019Sdes PRIVSEP(start_pam(authctxt)); 261157019Sdes#endif 262137019Sdes setproctitle("%s%s", authctxt->valid ? user : "unknown", 26398684Sdes use_privsep ? " [net]" : ""); 26469591Sgreen authctxt->service = xstrdup(service); 26592559Sdes authctxt->style = style ? xstrdup(style) : NULL; 26698684Sdes if (use_privsep) 26798684Sdes mm_inform_authserv(service, style); 268181111Sdes userauth_banner(); 269248619Sdes if (auth2_setup_methods_lists(authctxt) != 0) 270248619Sdes packet_disconnect("no authentication methods enabled"); 27192559Sdes } else if (strcmp(user, authctxt->user) != 0 || 27292559Sdes strcmp(service, authctxt->service) != 0) { 27392559Sdes packet_disconnect("Change of username or service not allowed: " 27492559Sdes "(%s,%s) -> (%s,%s)", 27592559Sdes authctxt->user, authctxt->service, user, service); 27660573Skris } 27799053Sdes 27899053Sdes#ifdef HAVE_LOGIN_CAP 279231584Sed if (authctxt->pw != NULL) { 280231584Sed lc = login_getpwclass(authctxt->pw); 281231584Sed if (lc == NULL) 282231584Sed lc = login_getclassbyname(NULL, authctxt->pw); 283231584Sed if (!auth_hostok(lc, from_host, from_ip)) { 284231584Sed logit("Denied connection for %.200s from %.200s [%.200s].", 285231584Sed authctxt->pw->pw_name, from_host, from_ip); 286231584Sed packet_disconnect("Sorry, you are not allowed to connect."); 287231584Sed } 288231584Sed if (!auth_timeok(lc, time(NULL))) { 289231584Sed logit("LOGIN %.200s REFUSED (TIME) FROM %.200s", 290231584Sed authctxt->pw->pw_name, from_host); 291231584Sed packet_disconnect("Logins not available right now."); 292231584Sed } 293231584Sed login_close(lc); 294231584Sed lc = NULL; 295231584Sed } 29699053Sdes#endif /* HAVE_LOGIN_CAP */ 29799053Sdes 29876262Sgreen /* reset state */ 29992559Sdes auth2_challenge_stop(authctxt); 300192595Sdes#ifdef JPAKE 301192595Sdes auth2_jpake_stop(authctxt); 302192595Sdes#endif 303124211Sdes 304124211Sdes#ifdef GSSAPI 305192595Sdes /* XXX move to auth2_gssapi_stop() */ 306124211Sdes dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 307124211Sdes dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 308124211Sdes#endif 309124211Sdes 31076262Sgreen authctxt->postponed = 0; 311226046Sdes authctxt->server_caused_failure = 0; 31268704Sgreen 31376262Sgreen /* try to authenticate user */ 314248619Sdes m = authmethod_lookup(authctxt, method); 315181111Sdes if (m != NULL && authctxt->failures < options.max_authtries) { 31669591Sgreen debug2("input_userauth_request: try method %s", method); 31769591Sgreen authenticated = m->userauth(authctxt); 31869591Sgreen } 319248619Sdes userauth_finish(authctxt, authenticated, method, NULL); 32069591Sgreen 32169591Sgreen xfree(service); 32269591Sgreen xfree(user); 32369591Sgreen xfree(method); 32469591Sgreen} 32569591Sgreen 32669591Sgreenvoid 327248619Sdesuserauth_finish(Authctxt *authctxt, int authenticated, const char *method, 328248619Sdes const char *submethod) 32969591Sgreen{ 33092559Sdes char *methods; 331248619Sdes int partial = 0; 33292559Sdes 33376262Sgreen if (!authctxt->valid && authenticated) 33476262Sgreen fatal("INTERNAL ERROR: authenticated invalid user %s", 33576262Sgreen authctxt->user); 336248619Sdes if (authenticated && authctxt->postponed) 337248619Sdes fatal("INTERNAL ERROR: authenticated and postponed"); 33869591Sgreen 33976262Sgreen /* Special handling for root */ 340113911Sdes if (authenticated && authctxt->pw->pw_uid == 0 && 341147005Sdes !auth_root_allowed(method)) { 34276262Sgreen authenticated = 0; 343147005Sdes#ifdef SSH_AUDIT_EVENTS 344147005Sdes PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); 345147005Sdes#endif 346147005Sdes } 34760573Skris 348248619Sdes if (authenticated && options.num_auth_methods != 0) { 349248619Sdes if (!auth2_update_methods_lists(authctxt, method)) { 350248619Sdes authenticated = 0; 351248619Sdes partial = 1; 352248619Sdes } 353248619Sdes } 354248619Sdes 355248619Sdes /* Log before sending the reply */ 356248619Sdes auth_log(authctxt, authenticated, partial, method, submethod, " ssh2"); 357248619Sdes 358248619Sdes if (authctxt->postponed) 359248619Sdes return; 360248619Sdes 36198941Sdes#ifdef USE_PAM 362147005Sdes if (options.use_pam && authenticated) { 363147005Sdes if (!PRIVSEP(do_pam_account())) { 364147005Sdes /* if PAM returned a message, send it to the user */ 365147005Sdes if (buffer_len(&loginmsg) > 0) { 366147005Sdes buffer_append(&loginmsg, "\0", 1); 367147005Sdes userauth_send_banner(buffer_ptr(&loginmsg)); 368147005Sdes packet_write_wait(); 369147005Sdes } 370147005Sdes fatal("Access denied for user %s by PAM account " 371149753Sdes "configuration", authctxt->user); 372147005Sdes } 373147005Sdes } 374124211Sdes#endif 37598941Sdes 376106130Sdes#ifdef _UNICOS 377106130Sdes if (authenticated && cray_access_denied(authctxt->user)) { 378106130Sdes authenticated = 0; 379248619Sdes fatal("Access denied for user %s.", authctxt->user); 380106130Sdes } 381106130Sdes#endif /* _UNICOS */ 382106130Sdes 38392559Sdes if (authenticated == 1) { 38492559Sdes /* turn off userauth */ 38592559Sdes dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 38692559Sdes packet_start(SSH2_MSG_USERAUTH_SUCCESS); 38792559Sdes packet_send(); 38892559Sdes packet_write_wait(); 38992559Sdes /* now we can break out */ 39092559Sdes authctxt->success = 1; 39192559Sdes } else { 392181111Sdes 393181111Sdes /* Allow initial try of "none" auth without failure penalty */ 394226046Sdes if (!authctxt->server_caused_failure && 395226046Sdes (authctxt->attempt > 1 || strcmp(method, "none") != 0)) 396181111Sdes authctxt->failures++; 397181111Sdes if (authctxt->failures >= options.max_authtries) { 398147005Sdes#ifdef SSH_AUDIT_EVENTS 399147005Sdes PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); 400147005Sdes#endif 40192559Sdes packet_disconnect(AUTH_FAIL_MSG, authctxt->user); 402147005Sdes } 403248619Sdes methods = authmethods_get(authctxt); 404248619Sdes debug3("%s: failure partial=%d next methods=\"%s\"", __func__, 405248619Sdes partial, methods); 40692559Sdes packet_start(SSH2_MSG_USERAUTH_FAILURE); 40792559Sdes packet_put_cstring(methods); 408248619Sdes packet_put_char(partial); 40992559Sdes packet_send(); 41092559Sdes packet_write_wait(); 41192559Sdes xfree(methods); 41292559Sdes } 41376262Sgreen} 41469591Sgreen 415248619Sdes/* 416248619Sdes * Checks whether method is allowed by at least one AuthenticationMethods 417248619Sdes * methods list. Returns 1 if allowed, or no methods lists configured. 418248619Sdes * 0 otherwise. 419248619Sdes */ 420248619Sdesstatic int 421248619Sdesmethod_allowed(Authctxt *authctxt, const char *method) 422248619Sdes{ 423248619Sdes u_int i; 424248619Sdes 425248619Sdes /* 426248619Sdes * NB. authctxt->num_auth_methods might be zero as a result of 427248619Sdes * auth2_setup_methods_lists(), so check the configuration. 428248619Sdes */ 429248619Sdes if (options.num_auth_methods == 0) 430248619Sdes return 1; 431248619Sdes for (i = 0; i < authctxt->num_auth_methods; i++) { 432248619Sdes if (list_starts_with(authctxt->auth_methods[i], method)) 433248619Sdes return 1; 434248619Sdes } 435248619Sdes return 0; 436248619Sdes} 437248619Sdes 43892559Sdesstatic char * 439248619Sdesauthmethods_get(Authctxt *authctxt) 44060573Skris{ 44192559Sdes Buffer b; 44269591Sgreen char *list; 443248619Sdes u_int i; 44460573Skris 44592559Sdes buffer_init(&b); 44698684Sdes for (i = 0; authmethods[i] != NULL; i++) { 44798684Sdes if (strcmp(authmethods[i]->name, "none") == 0) 44869591Sgreen continue; 449248619Sdes if (authmethods[i]->enabled == NULL || 450248619Sdes *(authmethods[i]->enabled) == 0) 451248619Sdes continue; 452248619Sdes if (!method_allowed(authctxt, authmethods[i]->name)) 453248619Sdes continue; 454248619Sdes if (buffer_len(&b) > 0) 455248619Sdes buffer_append(&b, ",", 1); 456248619Sdes buffer_append(&b, authmethods[i]->name, 457248619Sdes strlen(authmethods[i]->name)); 45869591Sgreen } 45992559Sdes buffer_append(&b, "\0", 1); 46092559Sdes list = xstrdup(buffer_ptr(&b)); 46192559Sdes buffer_free(&b); 46269591Sgreen return list; 46360573Skris} 46460573Skris 46592559Sdesstatic Authmethod * 466248619Sdesauthmethod_lookup(Authctxt *authctxt, const char *name) 46769591Sgreen{ 46898684Sdes int i; 46998684Sdes 47069591Sgreen if (name != NULL) 47198684Sdes for (i = 0; authmethods[i] != NULL; i++) 47298684Sdes if (authmethods[i]->enabled != NULL && 47398684Sdes *(authmethods[i]->enabled) != 0 && 474248619Sdes strcmp(name, authmethods[i]->name) == 0 && 475248619Sdes method_allowed(authctxt, authmethods[i]->name)) 47698684Sdes return authmethods[i]; 47798684Sdes debug2("Unrecognized authentication method name: %s", 47898684Sdes name ? name : "NULL"); 47969591Sgreen return NULL; 48069591Sgreen} 481181111Sdes 482248619Sdes/* 483248619Sdes * Check a comma-separated list of methods for validity. Is need_enable is 484248619Sdes * non-zero, then also require that the methods are enabled. 485248619Sdes * Returns 0 on success or -1 if the methods list is invalid. 486248619Sdes */ 487248619Sdesint 488248619Sdesauth2_methods_valid(const char *_methods, int need_enable) 489248619Sdes{ 490248619Sdes char *methods, *omethods, *method; 491248619Sdes u_int i, found; 492248619Sdes int ret = -1; 493248619Sdes 494248619Sdes if (*_methods == '\0') { 495248619Sdes error("empty authentication method list"); 496248619Sdes return -1; 497248619Sdes } 498248619Sdes omethods = methods = xstrdup(_methods); 499248619Sdes while ((method = strsep(&methods, ",")) != NULL) { 500248619Sdes for (found = i = 0; !found && authmethods[i] != NULL; i++) { 501248619Sdes if (strcmp(method, authmethods[i]->name) != 0) 502248619Sdes continue; 503248619Sdes if (need_enable) { 504248619Sdes if (authmethods[i]->enabled == NULL || 505248619Sdes *(authmethods[i]->enabled) == 0) { 506248619Sdes error("Disabled method \"%s\" in " 507248619Sdes "AuthenticationMethods list \"%s\"", 508248619Sdes method, _methods); 509248619Sdes goto out; 510248619Sdes } 511248619Sdes } 512248619Sdes found = 1; 513248619Sdes break; 514248619Sdes } 515248619Sdes if (!found) { 516248619Sdes error("Unknown authentication method \"%s\" in list", 517248619Sdes method); 518248619Sdes goto out; 519248619Sdes } 520248619Sdes } 521248619Sdes ret = 0; 522248619Sdes out: 523248619Sdes free(omethods); 524248619Sdes return ret; 525248619Sdes} 526248619Sdes 527248619Sdes/* 528248619Sdes * Prune the AuthenticationMethods supplied in the configuration, removing 529248619Sdes * any methods lists that include disabled methods. Note that this might 530248619Sdes * leave authctxt->num_auth_methods == 0, even when multiple required auth 531248619Sdes * has been requested. For this reason, all tests for whether multiple is 532248619Sdes * enabled should consult options.num_auth_methods directly. 533248619Sdes */ 534248619Sdesint 535248619Sdesauth2_setup_methods_lists(Authctxt *authctxt) 536248619Sdes{ 537248619Sdes u_int i; 538248619Sdes 539248619Sdes if (options.num_auth_methods == 0) 540248619Sdes return 0; 541248619Sdes debug3("%s: checking methods", __func__); 542248619Sdes authctxt->auth_methods = xcalloc(options.num_auth_methods, 543248619Sdes sizeof(*authctxt->auth_methods)); 544248619Sdes authctxt->num_auth_methods = 0; 545248619Sdes for (i = 0; i < options.num_auth_methods; i++) { 546248619Sdes if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { 547248619Sdes logit("Authentication methods list \"%s\" contains " 548248619Sdes "disabled method, skipping", 549248619Sdes options.auth_methods[i]); 550248619Sdes continue; 551248619Sdes } 552248619Sdes debug("authentication methods list %d: %s", 553248619Sdes authctxt->num_auth_methods, options.auth_methods[i]); 554248619Sdes authctxt->auth_methods[authctxt->num_auth_methods++] = 555248619Sdes xstrdup(options.auth_methods[i]); 556248619Sdes } 557248619Sdes if (authctxt->num_auth_methods == 0) { 558248619Sdes error("No AuthenticationMethods left after eliminating " 559248619Sdes "disabled methods"); 560248619Sdes return -1; 561248619Sdes } 562248619Sdes return 0; 563248619Sdes} 564248619Sdes 565248619Sdesstatic int 566248619Sdeslist_starts_with(const char *methods, const char *method) 567248619Sdes{ 568248619Sdes size_t l = strlen(method); 569248619Sdes 570248619Sdes if (strncmp(methods, method, l) != 0) 571248619Sdes return 0; 572248619Sdes if (methods[l] != ',' && methods[l] != '\0') 573248619Sdes return 0; 574248619Sdes return 1; 575248619Sdes} 576248619Sdes 577248619Sdes/* 578248619Sdes * Remove method from the start of a comma-separated list of methods. 579248619Sdes * Returns 0 if the list of methods did not start with that method or 1 580248619Sdes * if it did. 581248619Sdes */ 582248619Sdesstatic int 583248619Sdesremove_method(char **methods, const char *method) 584248619Sdes{ 585248619Sdes char *omethods = *methods; 586248619Sdes size_t l = strlen(method); 587248619Sdes 588248619Sdes if (!list_starts_with(omethods, method)) 589248619Sdes return 0; 590248619Sdes *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0)); 591248619Sdes free(omethods); 592248619Sdes return 1; 593248619Sdes} 594248619Sdes 595248619Sdes/* 596248619Sdes * Called after successful authentication. Will remove the successful method 597248619Sdes * from the start of each list in which it occurs. If it was the last method 598248619Sdes * in any list, then authentication is deemed successful. 599248619Sdes * Returns 1 if the method completed any authentication list or 0 otherwise. 600248619Sdes */ 601248619Sdesint 602248619Sdesauth2_update_methods_lists(Authctxt *authctxt, const char *method) 603248619Sdes{ 604248619Sdes u_int i, found = 0; 605248619Sdes 606248619Sdes debug3("%s: updating methods list after \"%s\"", __func__, method); 607248619Sdes for (i = 0; i < authctxt->num_auth_methods; i++) { 608248619Sdes if (!remove_method(&(authctxt->auth_methods[i]), method)) 609248619Sdes continue; 610248619Sdes found = 1; 611248619Sdes if (*authctxt->auth_methods[i] == '\0') { 612248619Sdes debug2("authentication methods list %d complete", i); 613248619Sdes return 1; 614248619Sdes } 615248619Sdes debug3("authentication methods list %d remaining: \"%s\"", 616248619Sdes i, authctxt->auth_methods[i]); 617248619Sdes } 618248619Sdes /* This should not happen, but would be bad if it did */ 619248619Sdes if (!found) 620248619Sdes fatal("%s: method not in AuthenticationMethods", __func__); 621248619Sdes return 0; 622248619Sdes} 623248619Sdes 624248619Sdes 625