1323124Sdes/* $OpenBSD: auth2.c,v 1.136 2016/05/02 08:49:03 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: stable/10/crypto/openssh/auth2.c 323124 2017-09-01 22:52:18Z 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" 45295367Sdes#include "misc.h" 4660573Skris#include "servconf.h" 4760573Skris#include "compat.h" 48162856Sdes#include "key.h" 49162856Sdes#include "hostfile.h" 5060573Skris#include "auth.h" 5160573Skris#include "dispatch.h" 5276262Sgreen#include "pathnames.h" 53147005Sdes#include "buffer.h" 54162856Sdes#include "canohost.h" 5560573Skris 56124211Sdes#ifdef GSSAPI 57124211Sdes#include "ssh-gss.h" 58124211Sdes#endif 59162856Sdes#include "monitor_wrap.h" 60124211Sdes 6160573Skris/* import */ 6260573Skrisextern ServerOptions options; 6376262Sgreenextern u_char *session_id2; 64124211Sdesextern u_int session_id2_len; 65147005Sdesextern Buffer loginmsg; 6660573Skris 6798684Sdes/* methods */ 6898684Sdes 6998684Sdesextern Authmethod method_none; 7098684Sdesextern Authmethod method_pubkey; 7198684Sdesextern Authmethod method_passwd; 7298684Sdesextern Authmethod method_kbdint; 7398684Sdesextern Authmethod method_hostbased; 74124211Sdes#ifdef GSSAPI 75124211Sdesextern Authmethod method_gssapi; 76124211Sdes#endif 7798684Sdes 7898684SdesAuthmethod *authmethods[] = { 7998684Sdes &method_none, 8098684Sdes &method_pubkey, 81124211Sdes#ifdef GSSAPI 82124211Sdes &method_gssapi, 83124211Sdes#endif 8498684Sdes &method_passwd, 8598684Sdes &method_kbdint, 8698684Sdes &method_hostbased, 8798684Sdes NULL 8869591Sgreen}; 8969591Sgreen 9060573Skris/* protocol */ 9160573Skris 92295367Sdesstatic int input_service_request(int, u_int32_t, void *); 93295367Sdesstatic int input_userauth_request(int, u_int32_t, void *); 9460573Skris 9560573Skris/* helper */ 96248619Sdesstatic Authmethod *authmethod_lookup(Authctxt *, const char *); 97248619Sdesstatic char *authmethods_get(Authctxt *authctxt); 9860573Skris 99255767Sdes#define MATCH_NONE 0 /* method or submethod mismatch */ 100255767Sdes#define MATCH_METHOD 1 /* method matches (no submethod specified) */ 101255767Sdes#define MATCH_BOTH 2 /* method and submethod match */ 102255767Sdes#define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ 103255767Sdesstatic int list_starts_with(const char *, const char *, const char *); 104255767Sdes 105181111Sdeschar * 106181111Sdesauth2_read_banner(void) 107181111Sdes{ 108181111Sdes struct stat st; 109181111Sdes char *banner = NULL; 110181111Sdes size_t len, n; 111181111Sdes int fd; 112181111Sdes 113181111Sdes if ((fd = open(options.banner, O_RDONLY)) == -1) 114181111Sdes return (NULL); 115181111Sdes if (fstat(fd, &st) == -1) { 116181111Sdes close(fd); 117181111Sdes return (NULL); 118181111Sdes } 119240075Sdes if (st.st_size <= 0 || st.st_size > 1*1024*1024) { 120181111Sdes close(fd); 121181111Sdes return (NULL); 122181111Sdes } 123181111Sdes 124181111Sdes len = (size_t)st.st_size; /* truncate */ 125181111Sdes banner = xmalloc(len + 1); 126181111Sdes n = atomicio(read, fd, banner, len); 127181111Sdes close(fd); 128181111Sdes 129181111Sdes if (n != len) { 130255767Sdes free(banner); 131181111Sdes return (NULL); 132181111Sdes } 133181111Sdes banner[n] = '\0'; 134181111Sdes 135181111Sdes return (banner); 136181111Sdes} 137181111Sdes 138181111Sdesvoid 139181111Sdesuserauth_send_banner(const char *msg) 140181111Sdes{ 141181111Sdes if (datafellows & SSH_BUG_BANNER) 142181111Sdes return; 143181111Sdes 144181111Sdes packet_start(SSH2_MSG_USERAUTH_BANNER); 145181111Sdes packet_put_cstring(msg); 146181111Sdes packet_put_cstring(""); /* language, unused */ 147181111Sdes packet_send(); 148181111Sdes debug("%s: sent", __func__); 149181111Sdes} 150181111Sdes 151181111Sdesstatic void 152181111Sdesuserauth_banner(void) 153181111Sdes{ 154181111Sdes char *banner = NULL; 155181111Sdes 156295367Sdes if (options.banner == NULL || (datafellows & SSH_BUG_BANNER) != 0) 157181111Sdes return; 158181111Sdes 159181111Sdes if ((banner = PRIVSEP(auth2_read_banner())) == NULL) 160181111Sdes goto done; 161181111Sdes userauth_send_banner(banner); 162181111Sdes 163181111Sdesdone: 164255767Sdes free(banner); 165181111Sdes} 166181111Sdes 16760573Skris/* 16869591Sgreen * loop until authctxt->success == TRUE 16960573Skris */ 170126277Sdesvoid 171126277Sdesdo_authentication2(Authctxt *authctxt) 17260573Skris{ 17392559Sdes dispatch_init(&dispatch_protocol_error); 17460573Skris dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); 17569591Sgreen dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); 17660573Skris} 17760573Skris 178162856Sdes/*ARGSUSED*/ 179295367Sdesstatic int 18092559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt) 18160573Skris{ 18269591Sgreen Authctxt *authctxt = ctxt; 18376262Sgreen u_int len; 184106130Sdes int acceptit = 0; 185221420Sdes char *service = packet_get_cstring(&len); 18692559Sdes packet_check_eom(); 18760573Skris 18869591Sgreen if (authctxt == NULL) 18969591Sgreen fatal("input_service_request: no authctxt"); 19069591Sgreen 19160573Skris if (strcmp(service, "ssh-userauth") == 0) { 19269591Sgreen if (!authctxt->success) { 193106130Sdes acceptit = 1; 19460573Skris /* now we can handle user-auth requests */ 19560573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); 19660573Skris } 19760573Skris } 19860573Skris /* XXX all other service requests are denied */ 19960573Skris 200106130Sdes if (acceptit) { 20160573Skris packet_start(SSH2_MSG_SERVICE_ACCEPT); 20260573Skris packet_put_cstring(service); 20360573Skris packet_send(); 20460573Skris packet_write_wait(); 20560573Skris } else { 20660573Skris debug("bad service request %s", service); 20760573Skris packet_disconnect("bad service request %s", service); 20860573Skris } 209255767Sdes free(service); 210295367Sdes return 0; 21160573Skris} 21260573Skris 213162856Sdes/*ARGSUSED*/ 214295367Sdesstatic int 21592559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt) 21660573Skris{ 21769591Sgreen Authctxt *authctxt = ctxt; 21869591Sgreen Authmethod *m = NULL; 21976262Sgreen char *user, *service, *method, *style = NULL; 22060573Skris int authenticated = 0; 22199053Sdes#ifdef HAVE_LOGIN_CAP 222323124Sdes struct ssh *ssh = active_state; /* XXX */ 22399053Sdes login_cap_t *lc; 22499053Sdes const char *from_host, *from_ip; 22599053Sdes#endif 22669591Sgreen if (authctxt == NULL) 22769591Sgreen fatal("input_userauth_request: no authctxt"); 22860573Skris 229221420Sdes user = packet_get_cstring(NULL); 230221420Sdes service = packet_get_cstring(NULL); 231221420Sdes method = packet_get_cstring(NULL); 23260573Skris debug("userauth-request for user %s service %s method %s", user, service, method); 23376262Sgreen debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); 23460573Skris 23576262Sgreen if ((style = strchr(user, ':')) != NULL) 23676262Sgreen *style++ = 0; 23776262Sgreen 23876262Sgreen if (authctxt->attempt++ == 0) { 23969591Sgreen /* setup auth context */ 24098684Sdes authctxt->pw = PRIVSEP(getpwnamallow(user)); 241128460Sdes authctxt->user = xstrdup(user); 24298684Sdes if (authctxt->pw && strcmp(service, "ssh-connection")==0) { 24369591Sgreen authctxt->valid = 1; 24469591Sgreen debug2("input_userauth_request: setting up authctxt for %s", user); 24569591Sgreen } else { 246137019Sdes logit("input_userauth_request: invalid user %s", user); 247124211Sdes authctxt->pw = fakepw(); 248147005Sdes#ifdef SSH_AUDIT_EVENTS 249147005Sdes PRIVSEP(audit_event(SSH_INVALID_USER)); 250147005Sdes#endif 25160573Skris } 252157019Sdes#ifdef USE_PAM 253157019Sdes if (options.use_pam) 254157019Sdes PRIVSEP(start_pam(authctxt)); 255157019Sdes#endif 256137019Sdes setproctitle("%s%s", authctxt->valid ? user : "unknown", 25798684Sdes use_privsep ? " [net]" : ""); 25869591Sgreen authctxt->service = xstrdup(service); 25992559Sdes authctxt->style = style ? xstrdup(style) : NULL; 26098684Sdes if (use_privsep) 26198684Sdes mm_inform_authserv(service, style); 262181111Sdes userauth_banner(); 263248619Sdes if (auth2_setup_methods_lists(authctxt) != 0) 264248619Sdes packet_disconnect("no authentication methods enabled"); 26592559Sdes } else if (strcmp(user, authctxt->user) != 0 || 26692559Sdes strcmp(service, authctxt->service) != 0) { 26792559Sdes packet_disconnect("Change of username or service not allowed: " 26892559Sdes "(%s,%s) -> (%s,%s)", 26992559Sdes authctxt->user, authctxt->service, user, service); 27060573Skris } 27199053Sdes 27299053Sdes#ifdef HAVE_LOGIN_CAP 273323124Sdes if (authctxt->pw != NULL && 274323124Sdes (lc = login_getpwclass(authctxt->pw)) != NULL) { 275323124Sdes logit("user %s login class %s", authctxt->pw->pw_name, 276323124Sdes authctxt->pw->pw_class); 277323124Sdes from_host = auth_get_canonical_hostname(ssh, options.use_dns); 278323124Sdes from_ip = ssh_remote_ipaddr(ssh); 279231584Sed if (!auth_hostok(lc, from_host, from_ip)) { 280231584Sed logit("Denied connection for %.200s from %.200s [%.200s].", 281231584Sed authctxt->pw->pw_name, from_host, from_ip); 282231584Sed packet_disconnect("Sorry, you are not allowed to connect."); 283231584Sed } 284231584Sed if (!auth_timeok(lc, time(NULL))) { 285231584Sed logit("LOGIN %.200s REFUSED (TIME) FROM %.200s", 286231584Sed authctxt->pw->pw_name, from_host); 287231584Sed packet_disconnect("Logins not available right now."); 288231584Sed } 289231584Sed login_close(lc); 290231584Sed } 29199053Sdes#endif /* HAVE_LOGIN_CAP */ 29299053Sdes 29376262Sgreen /* reset state */ 29492559Sdes auth2_challenge_stop(authctxt); 295124211Sdes 296124211Sdes#ifdef GSSAPI 297192595Sdes /* XXX move to auth2_gssapi_stop() */ 298124211Sdes dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 299124211Sdes dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 300124211Sdes#endif 301124211Sdes 30276262Sgreen authctxt->postponed = 0; 303226046Sdes authctxt->server_caused_failure = 0; 30468704Sgreen 30576262Sgreen /* try to authenticate user */ 306248619Sdes m = authmethod_lookup(authctxt, method); 307181111Sdes if (m != NULL && authctxt->failures < options.max_authtries) { 30869591Sgreen debug2("input_userauth_request: try method %s", method); 30969591Sgreen authenticated = m->userauth(authctxt); 31069591Sgreen } 311248619Sdes userauth_finish(authctxt, authenticated, method, NULL); 31269591Sgreen 313255767Sdes free(service); 314255767Sdes free(user); 315255767Sdes free(method); 316295367Sdes return 0; 31769591Sgreen} 31869591Sgreen 31969591Sgreenvoid 320248619Sdesuserauth_finish(Authctxt *authctxt, int authenticated, const char *method, 321248619Sdes const char *submethod) 32269591Sgreen{ 32392559Sdes char *methods; 324248619Sdes int partial = 0; 32592559Sdes 32676262Sgreen if (!authctxt->valid && authenticated) 32776262Sgreen fatal("INTERNAL ERROR: authenticated invalid user %s", 32876262Sgreen authctxt->user); 329248619Sdes if (authenticated && authctxt->postponed) 330248619Sdes fatal("INTERNAL ERROR: authenticated and postponed"); 33169591Sgreen 33276262Sgreen /* Special handling for root */ 333113911Sdes if (authenticated && authctxt->pw->pw_uid == 0 && 334147005Sdes !auth_root_allowed(method)) { 33576262Sgreen authenticated = 0; 336147005Sdes#ifdef SSH_AUDIT_EVENTS 337147005Sdes PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); 338147005Sdes#endif 339147005Sdes } 34060573Skris 341248619Sdes if (authenticated && options.num_auth_methods != 0) { 342255767Sdes if (!auth2_update_methods_lists(authctxt, method, submethod)) { 343248619Sdes authenticated = 0; 344248619Sdes partial = 1; 345248619Sdes } 346248619Sdes } 347248619Sdes 348248619Sdes /* Log before sending the reply */ 349255767Sdes auth_log(authctxt, authenticated, partial, method, submethod); 350248619Sdes 351248619Sdes if (authctxt->postponed) 352248619Sdes return; 353248619Sdes 35498941Sdes#ifdef USE_PAM 355147005Sdes if (options.use_pam && authenticated) { 356147005Sdes if (!PRIVSEP(do_pam_account())) { 357147005Sdes /* if PAM returned a message, send it to the user */ 358147005Sdes if (buffer_len(&loginmsg) > 0) { 359147005Sdes buffer_append(&loginmsg, "\0", 1); 360147005Sdes userauth_send_banner(buffer_ptr(&loginmsg)); 361147005Sdes packet_write_wait(); 362147005Sdes } 363147005Sdes fatal("Access denied for user %s by PAM account " 364149753Sdes "configuration", authctxt->user); 365147005Sdes } 366147005Sdes } 367124211Sdes#endif 36898941Sdes 369106130Sdes#ifdef _UNICOS 370106130Sdes if (authenticated && cray_access_denied(authctxt->user)) { 371106130Sdes authenticated = 0; 372248619Sdes fatal("Access denied for user %s.", authctxt->user); 373106130Sdes } 374106130Sdes#endif /* _UNICOS */ 375106130Sdes 37692559Sdes if (authenticated == 1) { 37792559Sdes /* turn off userauth */ 37892559Sdes dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 37992559Sdes packet_start(SSH2_MSG_USERAUTH_SUCCESS); 38092559Sdes packet_send(); 38192559Sdes packet_write_wait(); 38292559Sdes /* now we can break out */ 38392559Sdes authctxt->success = 1; 38492559Sdes } else { 385181111Sdes 386181111Sdes /* Allow initial try of "none" auth without failure penalty */ 387295367Sdes if (!partial && !authctxt->server_caused_failure && 388226046Sdes (authctxt->attempt > 1 || strcmp(method, "none") != 0)) 389181111Sdes authctxt->failures++; 390181111Sdes if (authctxt->failures >= options.max_authtries) { 391147005Sdes#ifdef SSH_AUDIT_EVENTS 392147005Sdes PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); 393147005Sdes#endif 394295367Sdes auth_maxtries_exceeded(authctxt); 395147005Sdes } 396248619Sdes methods = authmethods_get(authctxt); 397248619Sdes debug3("%s: failure partial=%d next methods=\"%s\"", __func__, 398248619Sdes partial, methods); 39992559Sdes packet_start(SSH2_MSG_USERAUTH_FAILURE); 40092559Sdes packet_put_cstring(methods); 401248619Sdes packet_put_char(partial); 40292559Sdes packet_send(); 40392559Sdes packet_write_wait(); 404255767Sdes free(methods); 40592559Sdes } 40676262Sgreen} 40769591Sgreen 408248619Sdes/* 409248619Sdes * Checks whether method is allowed by at least one AuthenticationMethods 410248619Sdes * methods list. Returns 1 if allowed, or no methods lists configured. 411248619Sdes * 0 otherwise. 412248619Sdes */ 413255767Sdesint 414255767Sdesauth2_method_allowed(Authctxt *authctxt, const char *method, 415255767Sdes const char *submethod) 416248619Sdes{ 417248619Sdes u_int i; 418248619Sdes 419248619Sdes /* 420248619Sdes * NB. authctxt->num_auth_methods might be zero as a result of 421248619Sdes * auth2_setup_methods_lists(), so check the configuration. 422248619Sdes */ 423248619Sdes if (options.num_auth_methods == 0) 424248619Sdes return 1; 425248619Sdes for (i = 0; i < authctxt->num_auth_methods; i++) { 426255767Sdes if (list_starts_with(authctxt->auth_methods[i], method, 427255767Sdes submethod) != MATCH_NONE) 428248619Sdes return 1; 429248619Sdes } 430248619Sdes return 0; 431248619Sdes} 432248619Sdes 43392559Sdesstatic char * 434248619Sdesauthmethods_get(Authctxt *authctxt) 43560573Skris{ 43692559Sdes Buffer b; 43769591Sgreen char *list; 438248619Sdes u_int i; 43960573Skris 44092559Sdes buffer_init(&b); 44198684Sdes for (i = 0; authmethods[i] != NULL; i++) { 44298684Sdes if (strcmp(authmethods[i]->name, "none") == 0) 44369591Sgreen continue; 444248619Sdes if (authmethods[i]->enabled == NULL || 445248619Sdes *(authmethods[i]->enabled) == 0) 446248619Sdes continue; 447255767Sdes if (!auth2_method_allowed(authctxt, authmethods[i]->name, 448255767Sdes NULL)) 449248619Sdes continue; 450248619Sdes if (buffer_len(&b) > 0) 451248619Sdes buffer_append(&b, ",", 1); 452248619Sdes buffer_append(&b, authmethods[i]->name, 453248619Sdes strlen(authmethods[i]->name)); 45469591Sgreen } 455323124Sdes if ((list = sshbuf_dup_string(&b)) == NULL) 456323124Sdes fatal("%s: sshbuf_dup_string failed", __func__); 45792559Sdes buffer_free(&b); 45869591Sgreen return list; 45960573Skris} 46060573Skris 46192559Sdesstatic Authmethod * 462248619Sdesauthmethod_lookup(Authctxt *authctxt, const char *name) 46369591Sgreen{ 46498684Sdes int i; 46598684Sdes 46669591Sgreen if (name != NULL) 46798684Sdes for (i = 0; authmethods[i] != NULL; i++) 46898684Sdes if (authmethods[i]->enabled != NULL && 46998684Sdes *(authmethods[i]->enabled) != 0 && 470248619Sdes strcmp(name, authmethods[i]->name) == 0 && 471255767Sdes auth2_method_allowed(authctxt, 472255767Sdes authmethods[i]->name, NULL)) 47398684Sdes return authmethods[i]; 47498684Sdes debug2("Unrecognized authentication method name: %s", 47598684Sdes name ? name : "NULL"); 47669591Sgreen return NULL; 47769591Sgreen} 478181111Sdes 479248619Sdes/* 480248619Sdes * Check a comma-separated list of methods for validity. Is need_enable is 481248619Sdes * non-zero, then also require that the methods are enabled. 482248619Sdes * Returns 0 on success or -1 if the methods list is invalid. 483248619Sdes */ 484248619Sdesint 485248619Sdesauth2_methods_valid(const char *_methods, int need_enable) 486248619Sdes{ 487255767Sdes char *methods, *omethods, *method, *p; 488248619Sdes u_int i, found; 489248619Sdes int ret = -1; 490248619Sdes 491248619Sdes if (*_methods == '\0') { 492248619Sdes error("empty authentication method list"); 493248619Sdes return -1; 494248619Sdes } 495248619Sdes omethods = methods = xstrdup(_methods); 496248619Sdes while ((method = strsep(&methods, ",")) != NULL) { 497248619Sdes for (found = i = 0; !found && authmethods[i] != NULL; i++) { 498255767Sdes if ((p = strchr(method, ':')) != NULL) 499255767Sdes *p = '\0'; 500248619Sdes if (strcmp(method, authmethods[i]->name) != 0) 501248619Sdes continue; 502248619Sdes if (need_enable) { 503248619Sdes if (authmethods[i]->enabled == NULL || 504248619Sdes *(authmethods[i]->enabled) == 0) { 505248619Sdes error("Disabled method \"%s\" in " 506248619Sdes "AuthenticationMethods list \"%s\"", 507248619Sdes method, _methods); 508248619Sdes goto out; 509248619Sdes } 510248619Sdes } 511248619Sdes found = 1; 512248619Sdes break; 513248619Sdes } 514248619Sdes if (!found) { 515248619Sdes error("Unknown authentication method \"%s\" in list", 516248619Sdes method); 517248619Sdes goto out; 518248619Sdes } 519248619Sdes } 520248619Sdes ret = 0; 521248619Sdes out: 522248619Sdes free(omethods); 523248619Sdes return ret; 524248619Sdes} 525248619Sdes 526248619Sdes/* 527248619Sdes * Prune the AuthenticationMethods supplied in the configuration, removing 528248619Sdes * any methods lists that include disabled methods. Note that this might 529248619Sdes * leave authctxt->num_auth_methods == 0, even when multiple required auth 530248619Sdes * has been requested. For this reason, all tests for whether multiple is 531248619Sdes * enabled should consult options.num_auth_methods directly. 532248619Sdes */ 533248619Sdesint 534248619Sdesauth2_setup_methods_lists(Authctxt *authctxt) 535248619Sdes{ 536248619Sdes u_int i; 537248619Sdes 538248619Sdes if (options.num_auth_methods == 0) 539248619Sdes return 0; 540248619Sdes debug3("%s: checking methods", __func__); 541248619Sdes authctxt->auth_methods = xcalloc(options.num_auth_methods, 542248619Sdes sizeof(*authctxt->auth_methods)); 543248619Sdes authctxt->num_auth_methods = 0; 544248619Sdes for (i = 0; i < options.num_auth_methods; i++) { 545248619Sdes if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { 546248619Sdes logit("Authentication methods list \"%s\" contains " 547248619Sdes "disabled method, skipping", 548248619Sdes options.auth_methods[i]); 549248619Sdes continue; 550248619Sdes } 551248619Sdes debug("authentication methods list %d: %s", 552248619Sdes authctxt->num_auth_methods, options.auth_methods[i]); 553248619Sdes authctxt->auth_methods[authctxt->num_auth_methods++] = 554248619Sdes xstrdup(options.auth_methods[i]); 555248619Sdes } 556248619Sdes if (authctxt->num_auth_methods == 0) { 557248619Sdes error("No AuthenticationMethods left after eliminating " 558248619Sdes "disabled methods"); 559248619Sdes return -1; 560248619Sdes } 561248619Sdes return 0; 562248619Sdes} 563248619Sdes 564248619Sdesstatic int 565255767Sdeslist_starts_with(const char *methods, const char *method, 566255767Sdes const char *submethod) 567248619Sdes{ 568248619Sdes size_t l = strlen(method); 569255767Sdes int match; 570255767Sdes const char *p; 571248619Sdes 572248619Sdes if (strncmp(methods, method, l) != 0) 573255767Sdes return MATCH_NONE; 574255767Sdes p = methods + l; 575255767Sdes match = MATCH_METHOD; 576255767Sdes if (*p == ':') { 577255767Sdes if (!submethod) 578255767Sdes return MATCH_PARTIAL; 579255767Sdes l = strlen(submethod); 580255767Sdes p += 1; 581255767Sdes if (strncmp(submethod, p, l)) 582255767Sdes return MATCH_NONE; 583255767Sdes p += l; 584255767Sdes match = MATCH_BOTH; 585255767Sdes } 586255767Sdes if (*p != ',' && *p != '\0') 587255767Sdes return MATCH_NONE; 588255767Sdes return match; 589248619Sdes} 590248619Sdes 591248619Sdes/* 592248619Sdes * Remove method from the start of a comma-separated list of methods. 593248619Sdes * Returns 0 if the list of methods did not start with that method or 1 594248619Sdes * if it did. 595248619Sdes */ 596248619Sdesstatic int 597255767Sdesremove_method(char **methods, const char *method, const char *submethod) 598248619Sdes{ 599255767Sdes char *omethods = *methods, *p; 600248619Sdes size_t l = strlen(method); 601255767Sdes int match; 602248619Sdes 603255767Sdes match = list_starts_with(omethods, method, submethod); 604255767Sdes if (match != MATCH_METHOD && match != MATCH_BOTH) 605248619Sdes return 0; 606255767Sdes p = omethods + l; 607255767Sdes if (submethod && match == MATCH_BOTH) 608255767Sdes p += 1 + strlen(submethod); /* include colon */ 609255767Sdes if (*p == ',') 610255767Sdes p++; 611255767Sdes *methods = xstrdup(p); 612248619Sdes free(omethods); 613248619Sdes return 1; 614248619Sdes} 615248619Sdes 616248619Sdes/* 617248619Sdes * Called after successful authentication. Will remove the successful method 618248619Sdes * from the start of each list in which it occurs. If it was the last method 619248619Sdes * in any list, then authentication is deemed successful. 620248619Sdes * Returns 1 if the method completed any authentication list or 0 otherwise. 621248619Sdes */ 622248619Sdesint 623255767Sdesauth2_update_methods_lists(Authctxt *authctxt, const char *method, 624255767Sdes const char *submethod) 625248619Sdes{ 626248619Sdes u_int i, found = 0; 627248619Sdes 628248619Sdes debug3("%s: updating methods list after \"%s\"", __func__, method); 629248619Sdes for (i = 0; i < authctxt->num_auth_methods; i++) { 630255767Sdes if (!remove_method(&(authctxt->auth_methods[i]), method, 631255767Sdes submethod)) 632248619Sdes continue; 633248619Sdes found = 1; 634248619Sdes if (*authctxt->auth_methods[i] == '\0') { 635248619Sdes debug2("authentication methods list %d complete", i); 636248619Sdes return 1; 637248619Sdes } 638248619Sdes debug3("authentication methods list %d remaining: \"%s\"", 639248619Sdes i, authctxt->auth_methods[i]); 640248619Sdes } 641248619Sdes /* This should not happen, but would be bad if it did */ 642248619Sdes if (!found) 643248619Sdes fatal("%s: method not in AuthenticationMethods", __func__); 644248619Sdes return 0; 645248619Sdes} 646248619Sdes 647248619Sdes 648