1255767Sdes/* $OpenBSD: auth2.c,v 1.129 2013/05/19 02:42:42 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$"); 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); 10360573Skris 104255767Sdes#define MATCH_NONE 0 /* method or submethod mismatch */ 105255767Sdes#define MATCH_METHOD 1 /* method matches (no submethod specified) */ 106255767Sdes#define MATCH_BOTH 2 /* method and submethod match */ 107255767Sdes#define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ 108255767Sdesstatic int list_starts_with(const char *, const char *, const char *); 109255767Sdes 110181111Sdeschar * 111181111Sdesauth2_read_banner(void) 112181111Sdes{ 113181111Sdes struct stat st; 114181111Sdes char *banner = NULL; 115181111Sdes size_t len, n; 116181111Sdes int fd; 117181111Sdes 118181111Sdes if ((fd = open(options.banner, O_RDONLY)) == -1) 119181111Sdes return (NULL); 120181111Sdes if (fstat(fd, &st) == -1) { 121181111Sdes close(fd); 122181111Sdes return (NULL); 123181111Sdes } 124240075Sdes if (st.st_size <= 0 || st.st_size > 1*1024*1024) { 125181111Sdes close(fd); 126181111Sdes return (NULL); 127181111Sdes } 128181111Sdes 129181111Sdes len = (size_t)st.st_size; /* truncate */ 130181111Sdes banner = xmalloc(len + 1); 131181111Sdes n = atomicio(read, fd, banner, len); 132181111Sdes close(fd); 133181111Sdes 134181111Sdes if (n != len) { 135255767Sdes free(banner); 136181111Sdes return (NULL); 137181111Sdes } 138181111Sdes banner[n] = '\0'; 139181111Sdes 140181111Sdes return (banner); 141181111Sdes} 142181111Sdes 143181111Sdesvoid 144181111Sdesuserauth_send_banner(const char *msg) 145181111Sdes{ 146181111Sdes if (datafellows & SSH_BUG_BANNER) 147181111Sdes return; 148181111Sdes 149181111Sdes packet_start(SSH2_MSG_USERAUTH_BANNER); 150181111Sdes packet_put_cstring(msg); 151181111Sdes packet_put_cstring(""); /* language, unused */ 152181111Sdes packet_send(); 153181111Sdes debug("%s: sent", __func__); 154181111Sdes} 155181111Sdes 156181111Sdesstatic void 157181111Sdesuserauth_banner(void) 158181111Sdes{ 159181111Sdes char *banner = NULL; 160181111Sdes 161181111Sdes if (options.banner == NULL || 162181111Sdes strcasecmp(options.banner, "none") == 0 || 163181111Sdes (datafellows & SSH_BUG_BANNER) != 0) 164181111Sdes return; 165181111Sdes 166181111Sdes if ((banner = PRIVSEP(auth2_read_banner())) == NULL) 167181111Sdes goto done; 168181111Sdes userauth_send_banner(banner); 169181111Sdes 170181111Sdesdone: 171255767Sdes free(banner); 172181111Sdes} 173181111Sdes 17460573Skris/* 17569591Sgreen * loop until authctxt->success == TRUE 17660573Skris */ 177126277Sdesvoid 178126277Sdesdo_authentication2(Authctxt *authctxt) 17960573Skris{ 18092559Sdes dispatch_init(&dispatch_protocol_error); 18160573Skris dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); 18269591Sgreen dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); 18360573Skris} 18460573Skris 185162856Sdes/*ARGSUSED*/ 18692559Sdesstatic void 18792559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt) 18860573Skris{ 18969591Sgreen Authctxt *authctxt = ctxt; 19076262Sgreen u_int len; 191106130Sdes int acceptit = 0; 192221420Sdes char *service = packet_get_cstring(&len); 19392559Sdes packet_check_eom(); 19460573Skris 19569591Sgreen if (authctxt == NULL) 19669591Sgreen fatal("input_service_request: no authctxt"); 19769591Sgreen 19860573Skris if (strcmp(service, "ssh-userauth") == 0) { 19969591Sgreen if (!authctxt->success) { 200106130Sdes acceptit = 1; 20160573Skris /* now we can handle user-auth requests */ 20260573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); 20360573Skris } 20460573Skris } 20560573Skris /* XXX all other service requests are denied */ 20660573Skris 207106130Sdes if (acceptit) { 20860573Skris packet_start(SSH2_MSG_SERVICE_ACCEPT); 20960573Skris packet_put_cstring(service); 21060573Skris packet_send(); 21160573Skris packet_write_wait(); 21260573Skris } else { 21360573Skris debug("bad service request %s", service); 21460573Skris packet_disconnect("bad service request %s", service); 21560573Skris } 216255767Sdes free(service); 21760573Skris} 21860573Skris 219162856Sdes/*ARGSUSED*/ 22092559Sdesstatic void 22192559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt) 22260573Skris{ 22369591Sgreen Authctxt *authctxt = ctxt; 22469591Sgreen Authmethod *m = NULL; 22576262Sgreen char *user, *service, *method, *style = NULL; 22660573Skris int authenticated = 0; 22799053Sdes#ifdef HAVE_LOGIN_CAP 22899053Sdes login_cap_t *lc; 22999053Sdes const char *from_host, *from_ip; 23060573Skris 231231584Sed from_host = get_canonical_hostname(options.use_dns); 232231584Sed from_ip = get_remote_ipaddr(); 23399053Sdes#endif 23499053Sdes 23569591Sgreen if (authctxt == NULL) 23669591Sgreen fatal("input_userauth_request: no authctxt"); 23760573Skris 238221420Sdes user = packet_get_cstring(NULL); 239221420Sdes service = packet_get_cstring(NULL); 240221420Sdes method = packet_get_cstring(NULL); 24160573Skris debug("userauth-request for user %s service %s method %s", user, service, method); 24276262Sgreen debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); 24360573Skris 24476262Sgreen if ((style = strchr(user, ':')) != NULL) 24576262Sgreen *style++ = 0; 24676262Sgreen 24776262Sgreen if (authctxt->attempt++ == 0) { 24869591Sgreen /* setup auth context */ 24998684Sdes authctxt->pw = PRIVSEP(getpwnamallow(user)); 250128460Sdes authctxt->user = xstrdup(user); 25198684Sdes if (authctxt->pw && strcmp(service, "ssh-connection")==0) { 25269591Sgreen authctxt->valid = 1; 25369591Sgreen debug2("input_userauth_request: setting up authctxt for %s", user); 25469591Sgreen } else { 255137019Sdes logit("input_userauth_request: invalid user %s", user); 256124211Sdes authctxt->pw = fakepw(); 257147005Sdes#ifdef SSH_AUDIT_EVENTS 258147005Sdes PRIVSEP(audit_event(SSH_INVALID_USER)); 259147005Sdes#endif 26060573Skris } 261157019Sdes#ifdef USE_PAM 262157019Sdes if (options.use_pam) 263157019Sdes PRIVSEP(start_pam(authctxt)); 264157019Sdes#endif 265137019Sdes setproctitle("%s%s", authctxt->valid ? user : "unknown", 26698684Sdes use_privsep ? " [net]" : ""); 26769591Sgreen authctxt->service = xstrdup(service); 26892559Sdes authctxt->style = style ? xstrdup(style) : NULL; 26998684Sdes if (use_privsep) 27098684Sdes mm_inform_authserv(service, style); 271181111Sdes userauth_banner(); 272248619Sdes if (auth2_setup_methods_lists(authctxt) != 0) 273248619Sdes packet_disconnect("no authentication methods enabled"); 27492559Sdes } else if (strcmp(user, authctxt->user) != 0 || 27592559Sdes strcmp(service, authctxt->service) != 0) { 27692559Sdes packet_disconnect("Change of username or service not allowed: " 27792559Sdes "(%s,%s) -> (%s,%s)", 27892559Sdes authctxt->user, authctxt->service, user, service); 27960573Skris } 28099053Sdes 28199053Sdes#ifdef HAVE_LOGIN_CAP 282231584Sed if (authctxt->pw != NULL) { 283231584Sed lc = login_getpwclass(authctxt->pw); 284231584Sed if (lc == NULL) 285231584Sed lc = login_getclassbyname(NULL, authctxt->pw); 286231584Sed if (!auth_hostok(lc, from_host, from_ip)) { 287231584Sed logit("Denied connection for %.200s from %.200s [%.200s].", 288231584Sed authctxt->pw->pw_name, from_host, from_ip); 289231584Sed packet_disconnect("Sorry, you are not allowed to connect."); 290231584Sed } 291231584Sed if (!auth_timeok(lc, time(NULL))) { 292231584Sed logit("LOGIN %.200s REFUSED (TIME) FROM %.200s", 293231584Sed authctxt->pw->pw_name, from_host); 294231584Sed packet_disconnect("Logins not available right now."); 295231584Sed } 296231584Sed login_close(lc); 297231584Sed lc = NULL; 298231584Sed } 29999053Sdes#endif /* HAVE_LOGIN_CAP */ 30099053Sdes 30176262Sgreen /* reset state */ 30292559Sdes auth2_challenge_stop(authctxt); 303192595Sdes#ifdef JPAKE 304192595Sdes auth2_jpake_stop(authctxt); 305192595Sdes#endif 306124211Sdes 307124211Sdes#ifdef GSSAPI 308192595Sdes /* XXX move to auth2_gssapi_stop() */ 309124211Sdes dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 310124211Sdes dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 311124211Sdes#endif 312124211Sdes 31376262Sgreen authctxt->postponed = 0; 314226046Sdes authctxt->server_caused_failure = 0; 31568704Sgreen 31676262Sgreen /* try to authenticate user */ 317248619Sdes m = authmethod_lookup(authctxt, method); 318181111Sdes if (m != NULL && authctxt->failures < options.max_authtries) { 31969591Sgreen debug2("input_userauth_request: try method %s", method); 32069591Sgreen authenticated = m->userauth(authctxt); 32169591Sgreen } 322248619Sdes userauth_finish(authctxt, authenticated, method, NULL); 32369591Sgreen 324255767Sdes free(service); 325255767Sdes free(user); 326255767Sdes free(method); 32769591Sgreen} 32869591Sgreen 32969591Sgreenvoid 330248619Sdesuserauth_finish(Authctxt *authctxt, int authenticated, const char *method, 331248619Sdes const char *submethod) 33269591Sgreen{ 33392559Sdes char *methods; 334248619Sdes int partial = 0; 33592559Sdes 33676262Sgreen if (!authctxt->valid && authenticated) 33776262Sgreen fatal("INTERNAL ERROR: authenticated invalid user %s", 33876262Sgreen authctxt->user); 339248619Sdes if (authenticated && authctxt->postponed) 340248619Sdes fatal("INTERNAL ERROR: authenticated and postponed"); 34169591Sgreen 34276262Sgreen /* Special handling for root */ 343113911Sdes if (authenticated && authctxt->pw->pw_uid == 0 && 344147005Sdes !auth_root_allowed(method)) { 34576262Sgreen authenticated = 0; 346147005Sdes#ifdef SSH_AUDIT_EVENTS 347147005Sdes PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); 348147005Sdes#endif 349147005Sdes } 35060573Skris 351248619Sdes if (authenticated && options.num_auth_methods != 0) { 352255767Sdes if (!auth2_update_methods_lists(authctxt, method, submethod)) { 353248619Sdes authenticated = 0; 354248619Sdes partial = 1; 355248619Sdes } 356248619Sdes } 357248619Sdes 358248619Sdes /* Log before sending the reply */ 359255767Sdes auth_log(authctxt, authenticated, partial, method, submethod); 360248619Sdes 361248619Sdes if (authctxt->postponed) 362248619Sdes return; 363248619Sdes 36498941Sdes#ifdef USE_PAM 365147005Sdes if (options.use_pam && authenticated) { 366147005Sdes if (!PRIVSEP(do_pam_account())) { 367147005Sdes /* if PAM returned a message, send it to the user */ 368147005Sdes if (buffer_len(&loginmsg) > 0) { 369147005Sdes buffer_append(&loginmsg, "\0", 1); 370147005Sdes userauth_send_banner(buffer_ptr(&loginmsg)); 371147005Sdes packet_write_wait(); 372147005Sdes } 373147005Sdes fatal("Access denied for user %s by PAM account " 374149753Sdes "configuration", authctxt->user); 375147005Sdes } 376147005Sdes } 377124211Sdes#endif 37898941Sdes 379106130Sdes#ifdef _UNICOS 380106130Sdes if (authenticated && cray_access_denied(authctxt->user)) { 381106130Sdes authenticated = 0; 382248619Sdes fatal("Access denied for user %s.", authctxt->user); 383106130Sdes } 384106130Sdes#endif /* _UNICOS */ 385106130Sdes 38692559Sdes if (authenticated == 1) { 38792559Sdes /* turn off userauth */ 38892559Sdes dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 38992559Sdes packet_start(SSH2_MSG_USERAUTH_SUCCESS); 39092559Sdes packet_send(); 39192559Sdes packet_write_wait(); 39292559Sdes /* now we can break out */ 39392559Sdes authctxt->success = 1; 39492559Sdes } else { 395181111Sdes 396181111Sdes /* Allow initial try of "none" auth without failure penalty */ 397226046Sdes if (!authctxt->server_caused_failure && 398226046Sdes (authctxt->attempt > 1 || strcmp(method, "none") != 0)) 399181111Sdes authctxt->failures++; 400181111Sdes if (authctxt->failures >= options.max_authtries) { 401147005Sdes#ifdef SSH_AUDIT_EVENTS 402147005Sdes PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); 403147005Sdes#endif 40492559Sdes packet_disconnect(AUTH_FAIL_MSG, authctxt->user); 405147005Sdes } 406248619Sdes methods = authmethods_get(authctxt); 407248619Sdes debug3("%s: failure partial=%d next methods=\"%s\"", __func__, 408248619Sdes partial, methods); 40992559Sdes packet_start(SSH2_MSG_USERAUTH_FAILURE); 41092559Sdes packet_put_cstring(methods); 411248619Sdes packet_put_char(partial); 41292559Sdes packet_send(); 41392559Sdes packet_write_wait(); 414255767Sdes free(methods); 41592559Sdes } 41676262Sgreen} 41769591Sgreen 418248619Sdes/* 419248619Sdes * Checks whether method is allowed by at least one AuthenticationMethods 420248619Sdes * methods list. Returns 1 if allowed, or no methods lists configured. 421248619Sdes * 0 otherwise. 422248619Sdes */ 423255767Sdesint 424255767Sdesauth2_method_allowed(Authctxt *authctxt, const char *method, 425255767Sdes const char *submethod) 426248619Sdes{ 427248619Sdes u_int i; 428248619Sdes 429248619Sdes /* 430248619Sdes * NB. authctxt->num_auth_methods might be zero as a result of 431248619Sdes * auth2_setup_methods_lists(), so check the configuration. 432248619Sdes */ 433248619Sdes if (options.num_auth_methods == 0) 434248619Sdes return 1; 435248619Sdes for (i = 0; i < authctxt->num_auth_methods; i++) { 436255767Sdes if (list_starts_with(authctxt->auth_methods[i], method, 437255767Sdes submethod) != MATCH_NONE) 438248619Sdes return 1; 439248619Sdes } 440248619Sdes return 0; 441248619Sdes} 442248619Sdes 44392559Sdesstatic char * 444248619Sdesauthmethods_get(Authctxt *authctxt) 44560573Skris{ 44692559Sdes Buffer b; 44769591Sgreen char *list; 448248619Sdes u_int i; 44960573Skris 45092559Sdes buffer_init(&b); 45198684Sdes for (i = 0; authmethods[i] != NULL; i++) { 45298684Sdes if (strcmp(authmethods[i]->name, "none") == 0) 45369591Sgreen continue; 454248619Sdes if (authmethods[i]->enabled == NULL || 455248619Sdes *(authmethods[i]->enabled) == 0) 456248619Sdes continue; 457255767Sdes if (!auth2_method_allowed(authctxt, authmethods[i]->name, 458255767Sdes NULL)) 459248619Sdes continue; 460248619Sdes if (buffer_len(&b) > 0) 461248619Sdes buffer_append(&b, ",", 1); 462248619Sdes buffer_append(&b, authmethods[i]->name, 463248619Sdes strlen(authmethods[i]->name)); 46469591Sgreen } 46592559Sdes buffer_append(&b, "\0", 1); 46692559Sdes list = xstrdup(buffer_ptr(&b)); 46792559Sdes buffer_free(&b); 46869591Sgreen return list; 46960573Skris} 47060573Skris 47192559Sdesstatic Authmethod * 472248619Sdesauthmethod_lookup(Authctxt *authctxt, const char *name) 47369591Sgreen{ 47498684Sdes int i; 47598684Sdes 47669591Sgreen if (name != NULL) 47798684Sdes for (i = 0; authmethods[i] != NULL; i++) 47898684Sdes if (authmethods[i]->enabled != NULL && 47998684Sdes *(authmethods[i]->enabled) != 0 && 480248619Sdes strcmp(name, authmethods[i]->name) == 0 && 481255767Sdes auth2_method_allowed(authctxt, 482255767Sdes authmethods[i]->name, NULL)) 48398684Sdes return authmethods[i]; 48498684Sdes debug2("Unrecognized authentication method name: %s", 48598684Sdes name ? name : "NULL"); 48669591Sgreen return NULL; 48769591Sgreen} 488181111Sdes 489248619Sdes/* 490248619Sdes * Check a comma-separated list of methods for validity. Is need_enable is 491248619Sdes * non-zero, then also require that the methods are enabled. 492248619Sdes * Returns 0 on success or -1 if the methods list is invalid. 493248619Sdes */ 494248619Sdesint 495248619Sdesauth2_methods_valid(const char *_methods, int need_enable) 496248619Sdes{ 497255767Sdes char *methods, *omethods, *method, *p; 498248619Sdes u_int i, found; 499248619Sdes int ret = -1; 500248619Sdes 501248619Sdes if (*_methods == '\0') { 502248619Sdes error("empty authentication method list"); 503248619Sdes return -1; 504248619Sdes } 505248619Sdes omethods = methods = xstrdup(_methods); 506248619Sdes while ((method = strsep(&methods, ",")) != NULL) { 507248619Sdes for (found = i = 0; !found && authmethods[i] != NULL; i++) { 508255767Sdes if ((p = strchr(method, ':')) != NULL) 509255767Sdes *p = '\0'; 510248619Sdes if (strcmp(method, authmethods[i]->name) != 0) 511248619Sdes continue; 512248619Sdes if (need_enable) { 513248619Sdes if (authmethods[i]->enabled == NULL || 514248619Sdes *(authmethods[i]->enabled) == 0) { 515248619Sdes error("Disabled method \"%s\" in " 516248619Sdes "AuthenticationMethods list \"%s\"", 517248619Sdes method, _methods); 518248619Sdes goto out; 519248619Sdes } 520248619Sdes } 521248619Sdes found = 1; 522248619Sdes break; 523248619Sdes } 524248619Sdes if (!found) { 525248619Sdes error("Unknown authentication method \"%s\" in list", 526248619Sdes method); 527248619Sdes goto out; 528248619Sdes } 529248619Sdes } 530248619Sdes ret = 0; 531248619Sdes out: 532248619Sdes free(omethods); 533248619Sdes return ret; 534248619Sdes} 535248619Sdes 536248619Sdes/* 537248619Sdes * Prune the AuthenticationMethods supplied in the configuration, removing 538248619Sdes * any methods lists that include disabled methods. Note that this might 539248619Sdes * leave authctxt->num_auth_methods == 0, even when multiple required auth 540248619Sdes * has been requested. For this reason, all tests for whether multiple is 541248619Sdes * enabled should consult options.num_auth_methods directly. 542248619Sdes */ 543248619Sdesint 544248619Sdesauth2_setup_methods_lists(Authctxt *authctxt) 545248619Sdes{ 546248619Sdes u_int i; 547248619Sdes 548248619Sdes if (options.num_auth_methods == 0) 549248619Sdes return 0; 550248619Sdes debug3("%s: checking methods", __func__); 551248619Sdes authctxt->auth_methods = xcalloc(options.num_auth_methods, 552248619Sdes sizeof(*authctxt->auth_methods)); 553248619Sdes authctxt->num_auth_methods = 0; 554248619Sdes for (i = 0; i < options.num_auth_methods; i++) { 555248619Sdes if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { 556248619Sdes logit("Authentication methods list \"%s\" contains " 557248619Sdes "disabled method, skipping", 558248619Sdes options.auth_methods[i]); 559248619Sdes continue; 560248619Sdes } 561248619Sdes debug("authentication methods list %d: %s", 562248619Sdes authctxt->num_auth_methods, options.auth_methods[i]); 563248619Sdes authctxt->auth_methods[authctxt->num_auth_methods++] = 564248619Sdes xstrdup(options.auth_methods[i]); 565248619Sdes } 566248619Sdes if (authctxt->num_auth_methods == 0) { 567248619Sdes error("No AuthenticationMethods left after eliminating " 568248619Sdes "disabled methods"); 569248619Sdes return -1; 570248619Sdes } 571248619Sdes return 0; 572248619Sdes} 573248619Sdes 574248619Sdesstatic int 575255767Sdeslist_starts_with(const char *methods, const char *method, 576255767Sdes const char *submethod) 577248619Sdes{ 578248619Sdes size_t l = strlen(method); 579255767Sdes int match; 580255767Sdes const char *p; 581248619Sdes 582248619Sdes if (strncmp(methods, method, l) != 0) 583255767Sdes return MATCH_NONE; 584255767Sdes p = methods + l; 585255767Sdes match = MATCH_METHOD; 586255767Sdes if (*p == ':') { 587255767Sdes if (!submethod) 588255767Sdes return MATCH_PARTIAL; 589255767Sdes l = strlen(submethod); 590255767Sdes p += 1; 591255767Sdes if (strncmp(submethod, p, l)) 592255767Sdes return MATCH_NONE; 593255767Sdes p += l; 594255767Sdes match = MATCH_BOTH; 595255767Sdes } 596255767Sdes if (*p != ',' && *p != '\0') 597255767Sdes return MATCH_NONE; 598255767Sdes return match; 599248619Sdes} 600248619Sdes 601248619Sdes/* 602248619Sdes * Remove method from the start of a comma-separated list of methods. 603248619Sdes * Returns 0 if the list of methods did not start with that method or 1 604248619Sdes * if it did. 605248619Sdes */ 606248619Sdesstatic int 607255767Sdesremove_method(char **methods, const char *method, const char *submethod) 608248619Sdes{ 609255767Sdes char *omethods = *methods, *p; 610248619Sdes size_t l = strlen(method); 611255767Sdes int match; 612248619Sdes 613255767Sdes match = list_starts_with(omethods, method, submethod); 614255767Sdes if (match != MATCH_METHOD && match != MATCH_BOTH) 615248619Sdes return 0; 616255767Sdes p = omethods + l; 617255767Sdes if (submethod && match == MATCH_BOTH) 618255767Sdes p += 1 + strlen(submethod); /* include colon */ 619255767Sdes if (*p == ',') 620255767Sdes p++; 621255767Sdes *methods = xstrdup(p); 622248619Sdes free(omethods); 623248619Sdes return 1; 624248619Sdes} 625248619Sdes 626248619Sdes/* 627248619Sdes * Called after successful authentication. Will remove the successful method 628248619Sdes * from the start of each list in which it occurs. If it was the last method 629248619Sdes * in any list, then authentication is deemed successful. 630248619Sdes * Returns 1 if the method completed any authentication list or 0 otherwise. 631248619Sdes */ 632248619Sdesint 633255767Sdesauth2_update_methods_lists(Authctxt *authctxt, const char *method, 634255767Sdes const char *submethod) 635248619Sdes{ 636248619Sdes u_int i, found = 0; 637248619Sdes 638248619Sdes debug3("%s: updating methods list after \"%s\"", __func__, method); 639248619Sdes for (i = 0; i < authctxt->num_auth_methods; i++) { 640255767Sdes if (!remove_method(&(authctxt->auth_methods[i]), method, 641255767Sdes submethod)) 642248619Sdes continue; 643248619Sdes found = 1; 644248619Sdes if (*authctxt->auth_methods[i] == '\0') { 645248619Sdes debug2("authentication methods list %d complete", i); 646248619Sdes return 1; 647248619Sdes } 648248619Sdes debug3("authentication methods list %d remaining: \"%s\"", 649248619Sdes i, authctxt->auth_methods[i]); 650248619Sdes } 651248619Sdes /* This should not happen, but would be bad if it did */ 652248619Sdes if (!found) 653248619Sdes fatal("%s: method not in AuthenticationMethods", __func__); 654248619Sdes return 0; 655248619Sdes} 656248619Sdes 657248619Sdes 658