summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--auth.h7
-rw-r--r--auth1.c7
-rw-r--r--auth2.c218
-rw-r--r--monitor.c35
-rw-r--r--servconf.c26
-rw-r--r--servconf.h7
-rw-r--r--sshd.c23
-rw-r--r--sshd_config.525
9 files changed, 328 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index 120c132af..85c92eb7f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,14 @@
7 [auth2-pubkey.c sshd.c sshd_config.5] 7 [auth2-pubkey.c sshd.c sshd_config.5]
8 Remove default of AuthorizedCommandUser. Administrators are now expected 8 Remove default of AuthorizedCommandUser. Administrators are now expected
9 to explicitly specify a user. feedback and ok markus@ 9 to explicitly specify a user. feedback and ok markus@
10 - djm@cvs.openbsd.org 2012/11/04 11:09:15
11 [auth.h auth1.c auth2.c monitor.c servconf.c servconf.h sshd.c]
12 [sshd_config.5]
13 Support multiple required authentication via an AuthenticationMethods
14 option. This option lists one or more comma-separated lists of
15 authentication method names. Successful completion of all the methods in
16 any list is required for authentication to complete;
17 feedback and ok markus@
10 18
1120121030 1920121030
12 - (djm) OpenBSD CVS Sync 20 - (djm) OpenBSD CVS Sync
diff --git a/auth.h b/auth.h
index 063404167..8920c7dae 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.h,v 1.70 2012/10/30 21:29:54 djm Exp $ */ 1/* $OpenBSD: auth.h,v 1.71 2012/11/04 11:09:15 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -64,6 +64,8 @@ struct Authctxt {
64#ifdef BSD_AUTH 64#ifdef BSD_AUTH
65 auth_session_t *as; 65 auth_session_t *as;
66#endif 66#endif
67 char **auth_methods; /* modified from server config */
68 u_int num_auth_methods;
67#ifdef KRB5 69#ifdef KRB5
68 krb5_context krb5_ctx; 70 krb5_context krb5_ctx;
69 krb5_ccache krb5_fwd_ccache; 71 krb5_ccache krb5_fwd_ccache;
@@ -152,6 +154,9 @@ void userauth_send_banner(const char *);
152int auth_root_allowed(char *); 154int auth_root_allowed(char *);
153 155
154char *auth2_read_banner(void); 156char *auth2_read_banner(void);
157int auth2_methods_valid(const char *, int);
158int auth2_update_methods_lists(Authctxt *, const char *);
159int auth2_setup_methods_lists(Authctxt *);
155 160
156void privsep_challenge_enable(void); 161void privsep_challenge_enable(void);
157 162
diff --git a/auth1.c b/auth1.c
index cc85aec74..fb37fadfe 100644
--- a/auth1.c
+++ b/auth1.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth1.c,v 1.75 2010/08/31 09:58:37 djm Exp $ */ 1/* $OpenBSD: auth1.c,v 1.76 2012/11/04 11:09:15 djm Exp $ */
2/* 2/*
3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved 4 * All rights reserved
@@ -406,6 +406,11 @@ do_authentication(Authctxt *authctxt)
406 authctxt->pw = fakepw(); 406 authctxt->pw = fakepw();
407 } 407 }
408 408
409 /* Configuration may have changed as a result of Match */
410 if (options.num_auth_methods != 0)
411 fatal("AuthenticationMethods is not supported with SSH "
412 "protocol 1");
413
409 setproctitle("%s%s", authctxt->valid ? user : "unknown", 414 setproctitle("%s%s", authctxt->valid ? user : "unknown",
410 use_privsep ? " [net]" : ""); 415 use_privsep ? " [net]" : "");
411 416
diff --git a/auth2.c b/auth2.c
index b66bef64c..8114ec863 100644
--- a/auth2.c
+++ b/auth2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth2.c,v 1.124 2011/12/07 05:44:38 djm Exp $ */ 1/* $OpenBSD: auth2.c,v 1.125 2012/11/04 11:09:15 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -96,8 +96,10 @@ static void input_service_request(int, u_int32_t, void *);
96static void input_userauth_request(int, u_int32_t, void *); 96static void input_userauth_request(int, u_int32_t, void *);
97 97
98/* helper */ 98/* helper */
99static Authmethod *authmethod_lookup(const char *); 99static Authmethod *authmethod_lookup(Authctxt *, const char *);
100static char *authmethods_get(void); 100static char *authmethods_get(Authctxt *authctxt);
101static int method_allowed(Authctxt *, const char *);
102static int list_starts_with(const char *, const char *);
101 103
102char * 104char *
103auth2_read_banner(void) 105auth2_read_banner(void)
@@ -255,6 +257,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
255 if (use_privsep) 257 if (use_privsep)
256 mm_inform_authserv(service, style); 258 mm_inform_authserv(service, style);
257 userauth_banner(); 259 userauth_banner();
260 if (auth2_setup_methods_lists(authctxt) != 0)
261 packet_disconnect("no authentication methods enabled");
258 } else if (strcmp(user, authctxt->user) != 0 || 262 } else if (strcmp(user, authctxt->user) != 0 ||
259 strcmp(service, authctxt->service) != 0) { 263 strcmp(service, authctxt->service) != 0) {
260 packet_disconnect("Change of username or service not allowed: " 264 packet_disconnect("Change of username or service not allowed: "
@@ -277,7 +281,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
277 authctxt->server_caused_failure = 0; 281 authctxt->server_caused_failure = 0;
278 282
279 /* try to authenticate user */ 283 /* try to authenticate user */
280 m = authmethod_lookup(method); 284 m = authmethod_lookup(authctxt, method);
281 if (m != NULL && authctxt->failures < options.max_authtries) { 285 if (m != NULL && authctxt->failures < options.max_authtries) {
282 debug2("input_userauth_request: try method %s", method); 286 debug2("input_userauth_request: try method %s", method);
283 authenticated = m->userauth(authctxt); 287 authenticated = m->userauth(authctxt);
@@ -293,6 +297,7 @@ void
293userauth_finish(Authctxt *authctxt, int authenticated, char *method) 297userauth_finish(Authctxt *authctxt, int authenticated, char *method)
294{ 298{
295 char *methods; 299 char *methods;
300 int partial = 0;
296 301
297 if (!authctxt->valid && authenticated) 302 if (!authctxt->valid && authenticated)
298 fatal("INTERNAL ERROR: authenticated invalid user %s", 303 fatal("INTERNAL ERROR: authenticated invalid user %s",
@@ -335,7 +340,13 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
335 if (authctxt->postponed) 340 if (authctxt->postponed)
336 return; 341 return;
337 342
338 /* XXX todo: check if multiple auth methods are needed */ 343 if (authenticated && options.num_auth_methods != 0) {
344 if (!auth2_update_methods_lists(authctxt, method)) {
345 authenticated = 0;
346 partial = 1;
347 }
348 }
349
339 if (authenticated == 1) { 350 if (authenticated == 1) {
340 /* turn off userauth */ 351 /* turn off userauth */
341 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 352 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
@@ -356,34 +367,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
356#endif 367#endif
357 packet_disconnect(AUTH_FAIL_MSG, authctxt->user); 368 packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
358 } 369 }
359 methods = authmethods_get(); 370 methods = authmethods_get(authctxt);
371 debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
372 partial, methods);
360 packet_start(SSH2_MSG_USERAUTH_FAILURE); 373 packet_start(SSH2_MSG_USERAUTH_FAILURE);
361 packet_put_cstring(methods); 374 packet_put_cstring(methods);
362 packet_put_char(0); /* XXX partial success, unused */ 375 packet_put_char(partial);
363 packet_send(); 376 packet_send();
364 packet_write_wait(); 377 packet_write_wait();
365 xfree(methods); 378 xfree(methods);
366 } 379 }
367} 380}
368 381
382/*
383 * Checks whether method is allowed by at least one AuthenticationMethods
384 * methods list. Returns 1 if allowed, or no methods lists configured.
385 * 0 otherwise.
386 */
387static int
388method_allowed(Authctxt *authctxt, const char *method)
389{
390 u_int i;
391
392 /*
393 * NB. authctxt->num_auth_methods might be zero as a result of
394 * auth2_setup_methods_lists(), so check the configuration.
395 */
396 if (options.num_auth_methods == 0)
397 return 1;
398 for (i = 0; i < authctxt->num_auth_methods; i++) {
399 if (list_starts_with(authctxt->auth_methods[i], method))
400 return 1;
401 }
402 return 0;
403}
404
369static char * 405static char *
370authmethods_get(void) 406authmethods_get(Authctxt *authctxt)
371{ 407{
372 Buffer b; 408 Buffer b;
373 char *list; 409 char *list;
374 int i; 410 u_int i;
375 411
376 buffer_init(&b); 412 buffer_init(&b);
377 for (i = 0; authmethods[i] != NULL; i++) { 413 for (i = 0; authmethods[i] != NULL; i++) {
378 if (strcmp(authmethods[i]->name, "none") == 0) 414 if (strcmp(authmethods[i]->name, "none") == 0)
379 continue; 415 continue;
380 if (authmethods[i]->enabled != NULL && 416 if (authmethods[i]->enabled == NULL ||
381 *(authmethods[i]->enabled) != 0) { 417 *(authmethods[i]->enabled) == 0)
382 if (buffer_len(&b) > 0) 418 continue;
383 buffer_append(&b, ",", 1); 419 if (!method_allowed(authctxt, authmethods[i]->name))
384 buffer_append(&b, authmethods[i]->name, 420 continue;
385 strlen(authmethods[i]->name)); 421 if (buffer_len(&b) > 0)
386 } 422 buffer_append(&b, ",", 1);
423 buffer_append(&b, authmethods[i]->name,
424 strlen(authmethods[i]->name));
387 } 425 }
388 buffer_append(&b, "\0", 1); 426 buffer_append(&b, "\0", 1);
389 list = xstrdup(buffer_ptr(&b)); 427 list = xstrdup(buffer_ptr(&b));
@@ -392,7 +430,7 @@ authmethods_get(void)
392} 430}
393 431
394static Authmethod * 432static Authmethod *
395authmethod_lookup(const char *name) 433authmethod_lookup(Authctxt *authctxt, const char *name)
396{ 434{
397 int i; 435 int i;
398 436
@@ -400,10 +438,154 @@ authmethod_lookup(const char *name)
400 for (i = 0; authmethods[i] != NULL; i++) 438 for (i = 0; authmethods[i] != NULL; i++)
401 if (authmethods[i]->enabled != NULL && 439 if (authmethods[i]->enabled != NULL &&
402 *(authmethods[i]->enabled) != 0 && 440 *(authmethods[i]->enabled) != 0 &&
403 strcmp(name, authmethods[i]->name) == 0) 441 strcmp(name, authmethods[i]->name) == 0 &&
442 method_allowed(authctxt, authmethods[i]->name))
404 return authmethods[i]; 443 return authmethods[i];
405 debug2("Unrecognized authentication method name: %s", 444 debug2("Unrecognized authentication method name: %s",
406 name ? name : "NULL"); 445 name ? name : "NULL");
407 return NULL; 446 return NULL;
408} 447}
409 448
449/*
450 * Check a comma-separated list of methods for validity. Is need_enable is
451 * non-zero, then also require that the methods are enabled.
452 * Returns 0 on success or -1 if the methods list is invalid.
453 */
454int
455auth2_methods_valid(const char *_methods, int need_enable)
456{
457 char *methods, *omethods, *method;
458 u_int i, found;
459 int ret = -1;
460
461 if (*_methods == '\0') {
462 error("empty authentication method list");
463 return -1;
464 }
465 omethods = methods = xstrdup(_methods);
466 while ((method = strsep(&methods, ",")) != NULL) {
467 for (found = i = 0; !found && authmethods[i] != NULL; i++) {
468 if (strcmp(method, authmethods[i]->name) != 0)
469 continue;
470 if (need_enable) {
471 if (authmethods[i]->enabled == NULL ||
472 *(authmethods[i]->enabled) == 0) {
473 error("Disabled method \"%s\" in "
474 "AuthenticationMethods list \"%s\"",
475 method, _methods);
476 goto out;
477 }
478 }
479 found = 1;
480 break;
481 }
482 if (!found) {
483 error("Unknown authentication method \"%s\" in list",
484 method);
485 goto out;
486 }
487 }
488 ret = 0;
489 out:
490 free(omethods);
491 return ret;
492}
493
494/*
495 * Prune the AuthenticationMethods supplied in the configuration, removing
496 * any methods lists that include disabled methods. Note that this might
497 * leave authctxt->num_auth_methods == 0, even when multiple required auth
498 * has been requested. For this reason, all tests for whether multiple is
499 * enabled should consult options.num_auth_methods directly.
500 */
501int
502auth2_setup_methods_lists(Authctxt *authctxt)
503{
504 u_int i;
505
506 if (options.num_auth_methods == 0)
507 return 0;
508 debug3("%s: checking methods", __func__);
509 authctxt->auth_methods = xcalloc(options.num_auth_methods,
510 sizeof(*authctxt->auth_methods));
511 authctxt->num_auth_methods = 0;
512 for (i = 0; i < options.num_auth_methods; i++) {
513 if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
514 logit("Authentication methods list \"%s\" contains "
515 "disabled method, skipping",
516 options.auth_methods[i]);
517 continue;
518 }
519 debug("authentication methods list %d: %s",
520 authctxt->num_auth_methods, options.auth_methods[i]);
521 authctxt->auth_methods[authctxt->num_auth_methods++] =
522 xstrdup(options.auth_methods[i]);
523 }
524 if (authctxt->num_auth_methods == 0) {
525 error("No AuthenticationMethods left after eliminating "
526 "disabled methods");
527 return -1;
528 }
529 return 0;
530}
531
532static int
533list_starts_with(const char *methods, const char *method)
534{
535 size_t l = strlen(method);
536
537 if (strncmp(methods, method, l) != 0)
538 return 0;
539 if (methods[l] != ',' && methods[l] != '\0')
540 return 0;
541 return 1;
542}
543
544/*
545 * Remove method from the start of a comma-separated list of methods.
546 * Returns 0 if the list of methods did not start with that method or 1
547 * if it did.
548 */
549static int
550remove_method(char **methods, const char *method)
551{
552 char *omethods = *methods;
553 size_t l = strlen(method);
554
555 if (!list_starts_with(omethods, method))
556 return 0;
557 *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
558 free(omethods);
559 return 1;
560}
561
562/*
563 * Called after successful authentication. Will remove the successful method
564 * from the start of each list in which it occurs. If it was the last method
565 * in any list, then authentication is deemed successful.
566 * Returns 1 if the method completed any authentication list or 0 otherwise.
567 */
568int
569auth2_update_methods_lists(Authctxt *authctxt, const char *method)
570{
571 u_int i, found = 0;
572
573 debug3("%s: updating methods list after \"%s\"", __func__, method);
574 for (i = 0; i < authctxt->num_auth_methods; i++) {
575 if (!remove_method(&(authctxt->auth_methods[i]), method))
576 continue;
577 found = 1;
578 if (*authctxt->auth_methods[i] == '\0') {
579 debug2("authentication methods list %d complete", i);
580 return 1;
581 }
582 debug3("authentication methods list %d remaining: \"%s\"",
583 i, authctxt->auth_methods[i]);
584 }
585 /* This should not happen, but would be bad if it did */
586 if (!found)
587 fatal("%s: method not in AuthenticationMethods", __func__);
588 return 0;
589}
590
591
diff --git a/monitor.c b/monitor.c
index e9802a3fd..0adbf3a65 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor.c,v 1.117 2012/06/22 12:30:26 dtucker Exp $ */ 1/* $OpenBSD: monitor.c,v 1.118 2012/11/04 11:09:15 djm Exp $ */
2/* 2/*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * Copyright 2002 Markus Friedl <markus@openbsd.org> 4 * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -381,6 +381,21 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
381 while (!authenticated) { 381 while (!authenticated) {
382 auth_method = "unknown"; 382 auth_method = "unknown";
383 authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); 383 authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
384
385 /* Special handling for multiple required authentications */
386 if (options.num_auth_methods != 0) {
387 if (!compat20)
388 fatal("AuthenticationMethods is not supported"
389 "with SSH protocol 1");
390 if (authenticated &&
391 !auth2_update_methods_lists(authctxt,
392 auth_method)) {
393 debug3("%s: method %s: partial", __func__,
394 auth_method);
395 authenticated = 0;
396 }
397 }
398
384 if (authenticated) { 399 if (authenticated) {
385 if (!(ent->flags & MON_AUTHDECIDE)) 400 if (!(ent->flags & MON_AUTHDECIDE))
386 fatal("%s: unexpected authentication from %d", 401 fatal("%s: unexpected authentication from %d",
@@ -401,7 +416,6 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
401 } 416 }
402#endif 417#endif
403 } 418 }
404
405 if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { 419 if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
406 auth_log(authctxt, authenticated, auth_method, 420 auth_log(authctxt, authenticated, auth_method,
407 compat20 ? " ssh2" : ""); 421 compat20 ? " ssh2" : "");
@@ -781,7 +795,17 @@ mm_answer_pwnamallow(int sock, Buffer *m)
781 COPY_MATCH_STRING_OPTS(); 795 COPY_MATCH_STRING_OPTS();
782#undef M_CP_STROPT 796#undef M_CP_STROPT
783#undef M_CP_STRARRAYOPT 797#undef M_CP_STRARRAYOPT
784 798
799 /* Create valid auth method lists */
800 if (compat20 && auth2_setup_methods_lists(authctxt) != 0) {
801 /*
802 * The monitor will continue long enough to let the child
803 * run to it's packet_disconnect(), but it must not allow any
804 * authentication to succeed.
805 */
806 debug("%s: no valid authentication method lists", __func__);
807 }
808
785 debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); 809 debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
786 mm_request_send(sock, MONITOR_ANS_PWNAM, m); 810 mm_request_send(sock, MONITOR_ANS_PWNAM, m);
787 811
@@ -918,7 +942,10 @@ mm_answer_bsdauthrespond(int sock, Buffer *m)
918 debug3("%s: sending authenticated: %d", __func__, authok); 942 debug3("%s: sending authenticated: %d", __func__, authok);
919 mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); 943 mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
920 944
921 auth_method = "bsdauth"; 945 if (compat20)
946 auth_method = "keyboard-interactive";
947 else
948 auth_method = "bsdauth";
922 949
923 return (authok != 0); 950 return (authok != 0);
924} 951}
diff --git a/servconf.c b/servconf.c
index 8e69ea5ce..b90dba63b 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,5 +1,5 @@
1 1
2/* $OpenBSD: servconf.c,v 1.231 2012/10/30 21:29:54 djm Exp $ */ 2/* $OpenBSD: servconf.c,v 1.232 2012/11/04 11:09:15 djm Exp $ */
3/* 3/*
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved 5 * All rights reserved
@@ -48,6 +48,8 @@
48#include "groupaccess.h" 48#include "groupaccess.h"
49#include "canohost.h" 49#include "canohost.h"
50#include "packet.h" 50#include "packet.h"
51#include "hostfile.h"
52#include "auth.h"
51 53
52static void add_listen_addr(ServerOptions *, char *, int); 54static void add_listen_addr(ServerOptions *, char *, int);
53static void add_one_listen_addr(ServerOptions *, char *, int); 55static void add_one_listen_addr(ServerOptions *, char *, int);
@@ -332,6 +334,7 @@ typedef enum {
332 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, 334 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
333 sKexAlgorithms, sIPQoS, sVersionAddendum, 335 sKexAlgorithms, sIPQoS, sVersionAddendum,
334 sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, 336 sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
337 sAuthenticationMethods,
335 sDeprecated, sUnsupported 338 sDeprecated, sUnsupported
336} ServerOpCodes; 339} ServerOpCodes;
337 340
@@ -459,6 +462,7 @@ static struct {
459 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, 462 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
460 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, 463 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
461 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, 464 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
465 { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
462 { NULL, sBadOption, 0 } 466 { NULL, sBadOption, 0 }
463}; 467};
464 468
@@ -1522,6 +1526,24 @@ process_server_config_line(ServerOptions *options, char *line,
1522 *charptr = xstrdup(arg); 1526 *charptr = xstrdup(arg);
1523 break; 1527 break;
1524 1528
1529 case sAuthenticationMethods:
1530 if (*activep && options->num_auth_methods == 0) {
1531 while ((arg = strdelim(&cp)) && *arg != '\0') {
1532 if (options->num_auth_methods >=
1533 MAX_AUTH_METHODS)
1534 fatal("%s line %d: "
1535 "too many authentication methods.",
1536 filename, linenum);
1537 if (auth2_methods_valid(arg, 0) != 0)
1538 fatal("%s line %d: invalid "
1539 "authentication method list.",
1540 filename, linenum);
1541 options->auth_methods[
1542 options->num_auth_methods++] = xstrdup(arg);
1543 }
1544 }
1545 return 0;
1546
1525 case sDeprecated: 1547 case sDeprecated:
1526 logit("%s line %d: Deprecated option %s", 1548 logit("%s line %d: Deprecated option %s",
1527 filename, linenum, arg); 1549 filename, linenum, arg);
@@ -1953,6 +1975,8 @@ dump_config(ServerOptions *o)
1953 dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); 1975 dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
1954 dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); 1976 dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups);
1955 dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); 1977 dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env);
1978 dump_cfg_strarray_oneline(sAuthenticationMethods,
1979 o->num_auth_methods, o->auth_methods);
1956 1980
1957 /* other arguments */ 1981 /* other arguments */
1958 for (i = 0; i < o->num_subsystems; i++) 1982 for (i = 0; i < o->num_subsystems; i++)
diff --git a/servconf.h b/servconf.h
index 0064c9bc5..68fcdb764 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.h,v 1.104 2012/10/30 21:29:55 djm Exp $ */ 1/* $OpenBSD: servconf.h,v 1.105 2012/11/04 11:09:15 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -28,6 +28,7 @@
28#define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ 28#define MAX_ACCEPT_ENV 256 /* Max # of env vars. */
29#define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ 29#define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */
30#define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */ 30#define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */
31#define MAX_AUTH_METHODS 256 /* Max # of AuthenticationMethods. */
31 32
32/* permit_root_login */ 33/* permit_root_login */
33#define PERMIT_NOT_SET -1 34#define PERMIT_NOT_SET -1
@@ -170,6 +171,9 @@ typedef struct {
170 char *authorized_keys_command_user; 171 char *authorized_keys_command_user;
171 172
172 char *version_addendum; /* Appended to SSH banner */ 173 char *version_addendum; /* Appended to SSH banner */
174
175 u_int num_auth_methods;
176 char *auth_methods[MAX_AUTH_METHODS];
173} ServerOptions; 177} ServerOptions;
174 178
175/* Information about the incoming connection as used by Match */ 179/* Information about the incoming connection as used by Match */
@@ -199,6 +203,7 @@ struct connection_info {
199 M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \ 203 M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \
200 M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ 204 M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \
201 M_CP_STRARRAYOPT(accept_env, num_accept_env); \ 205 M_CP_STRARRAYOPT(accept_env, num_accept_env); \
206 M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
202 } while (0) 207 } while (0)
203 208
204struct connection_info *get_connection_info(int, int); 209struct connection_info *get_connection_info(int, int);
diff --git a/sshd.c b/sshd.c
index 4ad1a4bd1..af7ff91ba 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshd.c,v 1.395 2012/11/04 10:38:43 djm Exp $ */ 1/* $OpenBSD: sshd.c,v 1.396 2012/11/04 11:09:15 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1337,6 +1337,7 @@ main(int ac, char **av)
1337 int remote_port; 1337 int remote_port;
1338 char *line; 1338 char *line;
1339 int config_s[2] = { -1 , -1 }; 1339 int config_s[2] = { -1 , -1 };
1340 u_int n;
1340 u_int64_t ibytes, obytes; 1341 u_int64_t ibytes, obytes;
1341 mode_t new_umask; 1342 mode_t new_umask;
1342 Key *key; 1343 Key *key;
@@ -1566,6 +1567,26 @@ main(int ac, char **av)
1566 fatal("AuthorizedKeysCommand set without " 1567 fatal("AuthorizedKeysCommand set without "
1567 "AuthorizedKeysCommandUser"); 1568 "AuthorizedKeysCommandUser");
1568 1569
1570 /*
1571 * Check whether there is any path through configured auth methods.
1572 * Unfortunately it is not possible to verify this generally before
1573 * daemonisation in the presence of Match block, but this catches
1574 * and warns for trivial misconfigurations that could break login.
1575 */
1576 if (options.num_auth_methods != 0) {
1577 if ((options.protocol & SSH_PROTO_1))
1578 fatal("AuthenticationMethods is not supported with "
1579 "SSH protocol 1");
1580 for (n = 0; n < options.num_auth_methods; n++) {
1581 if (auth2_methods_valid(options.auth_methods[n],
1582 1) == 0)
1583 break;
1584 }
1585 if (n >= options.num_auth_methods)
1586 fatal("AuthenticationMethods cannot be satisfied by "
1587 "enabled authentication methods");
1588 }
1589
1569 /* set default channel AF */ 1590 /* set default channel AF */
1570 channel_set_af(options.address_family); 1591 channel_set_af(options.address_family);
1571 1592
diff --git a/sshd_config.5 b/sshd_config.5
index 0fb0b837d..05f3374fb 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -33,7 +33,7 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: sshd_config.5,v 1.148 2012/11/04 10:38:43 djm Exp $ 36.\" $OpenBSD: sshd_config.5,v 1.149 2012/11/04 11:09:15 djm Exp $
37.Dd $Mdocdate: November 4 2012 $ 37.Dd $Mdocdate: November 4 2012 $
38.Dt SSHD_CONFIG 5 38.Dt SSHD_CONFIG 5
39.Os 39.Os
@@ -151,6 +151,28 @@ See
151in 151in
152.Xr ssh_config 5 152.Xr ssh_config 5
153for more information on patterns. 153for more information on patterns.
154.It Cm AuthenticationMethods
155Specifies the authentication methods that must be successfully completed
156for a user to be granted access.
157This option must be followed by one or more comma-separated lists of
158authentication method names.
159Successful authentication requires completion of every method in at least
160one of these lists.
161.Pp
162For example, an argument of
163.Dq publickey,password publickey,keyboard-interactive
164would require the user to complete public key authentication, followed by
165either password or keyboard interactive authentication.
166Only methods that are next in one or more lists are offered at each stage,
167so for this example, it would not be possible to attempt password or
168keyboard-interactive authentication before public key.
169.Pp
170This option is only available for SSH protocol 2 and will yield a fatal
171error if enabled if protocol 1 is also enabled.
172Note that each authentication method listed should also be explicitly enabled
173in the configuration.
174The default is not to require multiple authentication; successful completion
175of a single authentication method is sufficient.
154.It Cm AuthorizedKeysCommand 176.It Cm AuthorizedKeysCommand
155Specifies a program to be used to look up the user's public keys. 177Specifies a program to be used to look up the user's public keys.
156The program will be invoked with a single argument of the username 178The program will be invoked with a single argument of the username
@@ -728,6 +750,7 @@ Available keywords are
728.Cm AllowGroups , 750.Cm AllowGroups ,
729.Cm AllowTcpForwarding , 751.Cm AllowTcpForwarding ,
730.Cm AllowUsers , 752.Cm AllowUsers ,
753.Cm AuthenticationMethods ,
731.Cm AuthorizedKeysCommand , 754.Cm AuthorizedKeysCommand ,
732.Cm AuthorizedKeysCommandUser , 755.Cm AuthorizedKeysCommandUser ,
733.Cm AuthorizedKeysFile , 756.Cm AuthorizedKeysFile ,