vishalmishra434 / rpms / openssh

Forked from rpms/openssh a month ago
Clone
Petr Lautrbach bffd1c
diff --git a/auth.c b/auth.c
Petr Lautrbach bffd1c
index ee0cb05..1b2fc2b 100644
Petr Lautrbach bffd1c
--- a/auth.c
Petr Lautrbach bffd1c
+++ b/auth.c
Petr Lautrbach bffd1c
@@ -251,7 +251,8 @@ allowed_user(struct passwd * pw)
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 void
Petr Lautrbach bffd1c
-auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
Petr Lautrbach bffd1c
+auth_log(Authctxt *authctxt, int authenticated, int partial,
Petr Lautrbach bffd1c
+    const char *method, const char *submethod, const char *info)
Petr Lautrbach bffd1c
 {
Petr Lautrbach bffd1c
 	void (*authlog) (const char *fmt,...) = verbose;
Petr Lautrbach bffd1c
 	char *authmsg;
Petr Lautrbach bffd1c
@@ -268,12 +269,15 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	if (authctxt->postponed)
Petr Lautrbach bffd1c
 		authmsg = "Postponed";
Petr Lautrbach bffd1c
+	else if (partial)
Petr Lautrbach bffd1c
+		authmsg = "Partial";
Petr Lautrbach bffd1c
 	else
Petr Lautrbach bffd1c
 		authmsg = authenticated ? "Accepted" : "Failed";
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
-	authlog("%s %s for %s%.100s from %.200s port %d%s",
Petr Lautrbach bffd1c
+	authlog("%s %s%s%s for %s%.100s from %.200s port %d%s",
Petr Lautrbach bffd1c
 	    authmsg,
Petr Lautrbach bffd1c
 	    method,
Petr Lautrbach bffd1c
+	    submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
Petr Lautrbach bffd1c
 	    authctxt->valid ? "" : "invalid user ",
Petr Lautrbach bffd1c
 	    authctxt->user,
Petr Lautrbach bffd1c
 	    get_remote_ipaddr(),
Petr Lautrbach bffd1c
@@ -303,7 +307,7 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
Petr Lautrbach bffd1c
  * Check whether root logins are disallowed.
Petr Lautrbach bffd1c
  */
Petr Lautrbach bffd1c
 int
Petr Lautrbach bffd1c
-auth_root_allowed(char *method)
Petr Lautrbach bffd1c
+auth_root_allowed(const char *method)
Petr Lautrbach bffd1c
 {
Petr Lautrbach bffd1c
 	switch (options.permit_root_login) {
Petr Lautrbach bffd1c
 	case PERMIT_YES:
Petr Lautrbach bffd1c
diff --git a/auth.h b/auth.h
Petr Lautrbach bffd1c
index 0d786c4..29823bb 100644
Petr Lautrbach bffd1c
--- a/auth.h
Petr Lautrbach bffd1c
+++ b/auth.h
Petr Lautrbach bffd1c
@@ -64,6 +64,8 @@ struct Authctxt {
Petr Lautrbach bffd1c
 #ifdef BSD_AUTH
Petr Lautrbach bffd1c
 	auth_session_t	*as;
Petr Lautrbach bffd1c
 #endif
Petr Lautrbach bffd1c
+	char		**auth_methods;	/* modified from server config */
Petr Lautrbach bffd1c
+	u_int		 num_auth_methods;
Petr Lautrbach bffd1c
 #ifdef KRB5
Petr Lautrbach bffd1c
 	krb5_context	 krb5_ctx;
Petr Lautrbach bffd1c
 	krb5_ccache	 krb5_fwd_ccache;
Petr Lautrbach bffd1c
@@ -142,12 +144,17 @@ void disable_forwarding(void);
Petr Lautrbach bffd1c
 void	do_authentication(Authctxt *);
Petr Lautrbach bffd1c
 void	do_authentication2(Authctxt *);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
-void	auth_log(Authctxt *, int, char *, char *);
Petr Lautrbach bffd1c
-void	userauth_finish(Authctxt *, int, char *);
Petr Lautrbach bffd1c
+void	auth_log(Authctxt *, int, int, const char *, const char *,
Petr Lautrbach bffd1c
+    const char *);
Petr Lautrbach bffd1c
+void	userauth_finish(Authctxt *, int, const char *, const char *);
Petr Lautrbach bffd1c
+int	auth_root_allowed(const char *);
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 void	userauth_send_banner(const char *);
Petr Lautrbach bffd1c
-int	auth_root_allowed(char *);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 char	*auth2_read_banner(void);
Petr Lautrbach bffd1c
+int	 auth2_methods_valid(const char *, int);
Petr Lautrbach bffd1c
+int	 auth2_update_methods_lists(Authctxt *, const char *);
Petr Lautrbach bffd1c
+int	 auth2_setup_methods_lists(Authctxt *);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 void	privsep_challenge_enable(void);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
diff --git a/auth1.c b/auth1.c
Petr Lautrbach bffd1c
index cc85aec..458a110 100644
Petr Lautrbach bffd1c
--- a/auth1.c
Petr Lautrbach bffd1c
+++ b/auth1.c
Petr Lautrbach bffd1c
@@ -253,7 +253,8 @@ do_authloop(Authctxt *authctxt)
Petr Lautrbach bffd1c
 		if (options.use_pam && (PRIVSEP(do_pam_account())))
Petr Lautrbach bffd1c
 #endif
Petr Lautrbach bffd1c
 		{
Petr Lautrbach bffd1c
-			auth_log(authctxt, 1, "without authentication", "");
Petr Lautrbach bffd1c
+			auth_log(authctxt, 1, 0, "without authentication",
Petr Lautrbach bffd1c
+			    NULL, "");
Petr Lautrbach bffd1c
 			return;
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
@@ -352,7 +353,8 @@ do_authloop(Authctxt *authctxt)
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
  skip:
Petr Lautrbach bffd1c
 		/* Log before sending the reply */
Petr Lautrbach bffd1c
-		auth_log(authctxt, authenticated, get_authname(type), info);
Petr Lautrbach bffd1c
+		auth_log(authctxt, authenticated, 0, get_authname(type),
Petr Lautrbach bffd1c
+		    NULL, info);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 		if (client_user != NULL) {
Petr Lautrbach bffd1c
 			xfree(client_user);
Petr Lautrbach bffd1c
@@ -406,6 +408,11 @@ do_authentication(Authctxt *authctxt)
Petr Lautrbach bffd1c
 		authctxt->pw = fakepw();
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
+	/* Configuration may have changed as a result of Match */
Petr Lautrbach bffd1c
+	if (options.num_auth_methods != 0)
Petr Lautrbach bffd1c
+		fatal("AuthenticationMethods is not supported with SSH "
Petr Lautrbach bffd1c
+		    "protocol 1");
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 	setproctitle("%s%s", authctxt->valid ? user : "unknown",
Petr Lautrbach bffd1c
 	    use_privsep ? " [net]" : "");
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
diff --git a/auth2-chall.c b/auth2-chall.c
Petr Lautrbach bffd1c
index e6dbffe..5f7ec6d 100644
Petr Lautrbach bffd1c
--- a/auth2-chall.c
Petr Lautrbach bffd1c
+++ b/auth2-chall.c
Petr Lautrbach bffd1c
@@ -283,7 +283,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 	KbdintAuthctxt *kbdintctxt;
Petr Lautrbach bffd1c
 	int authenticated = 0, res;
Petr Lautrbach bffd1c
 	u_int i, nresp;
Petr Lautrbach bffd1c
-	char **response = NULL, *method;
Petr Lautrbach bffd1c
+	char *devicename = NULL, **response = NULL;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	if (authctxt == NULL)
Petr Lautrbach bffd1c
 		fatal("input_userauth_info_response: no authctxt");
Petr Lautrbach bffd1c
@@ -329,9 +329,7 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 		/* Failure! */
Petr Lautrbach bffd1c
 		break;
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
-
Petr Lautrbach bffd1c
-	xasprintf(&method, "keyboard-interactive/%s", kbdintctxt->device->name);
Petr Lautrbach bffd1c
-
Petr Lautrbach bffd1c
+	devicename = kbdintctxt->device->name;
Petr Lautrbach bffd1c
 	if (!authctxt->postponed) {
Petr Lautrbach bffd1c
 		if (authenticated) {
Petr Lautrbach bffd1c
 			auth2_challenge_stop(authctxt);
Petr Lautrbach bffd1c
@@ -341,8 +339,8 @@ input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 			auth2_challenge_start(authctxt);
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
-	userauth_finish(authctxt, authenticated, method);
Petr Lautrbach bffd1c
-	xfree(method);
Petr Lautrbach bffd1c
+	userauth_finish(authctxt, authenticated, "keyboard-interactive",
Petr Lautrbach bffd1c
+	    devicename);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 void
Petr Lautrbach bffd1c
diff --git a/auth2-gss.c b/auth2-gss.c
Petr Lautrbach bffd1c
index 0d59b21..338c748 100644
Petr Lautrbach bffd1c
--- a/auth2-gss.c
Petr Lautrbach bffd1c
+++ b/auth2-gss.c
Petr Lautrbach bffd1c
@@ -163,7 +163,7 @@ input_gssapi_token(int type, u_int32_t plen, void *ctxt)
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
 		authctxt->postponed = 0;
Petr Lautrbach bffd1c
 		dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
Petr Lautrbach bffd1c
-		userauth_finish(authctxt, 0, "gssapi-with-mic");
Petr Lautrbach bffd1c
+		userauth_finish(authctxt, 0, "gssapi-with-mic", NULL);
Petr Lautrbach bffd1c
 	} else {
Petr Lautrbach bffd1c
 		if (send_tok.length != 0) {
Petr Lautrbach bffd1c
 			packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
Petr Lautrbach bffd1c
@@ -251,7 +251,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
Petr Lautrbach bffd1c
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
Petr Lautrbach bffd1c
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
Petr Lautrbach bffd1c
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
Petr Lautrbach bffd1c
-	userauth_finish(authctxt, authenticated, "gssapi-with-mic");
Petr Lautrbach bffd1c
+	userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 static void
Petr Lautrbach bffd1c
@@ -291,7 +291,7 @@ input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
Petr Lautrbach bffd1c
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
Petr Lautrbach bffd1c
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
Petr Lautrbach bffd1c
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
Petr Lautrbach bffd1c
-	userauth_finish(authctxt, authenticated, "gssapi-with-mic");
Petr Lautrbach bffd1c
+	userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 Authmethod method_gssapi = {
Petr Lautrbach bffd1c
diff --git a/auth2-jpake.c b/auth2-jpake.c
Petr Lautrbach bffd1c
index a460e82..e4ba9aa 100644
Petr Lautrbach bffd1c
--- a/auth2-jpake.c
Petr Lautrbach bffd1c
+++ b/auth2-jpake.c
Petr Lautrbach bffd1c
@@ -556,7 +556,7 @@ input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 	authctxt->postponed = 0;
Petr Lautrbach bffd1c
 	jpake_free(authctxt->jpake_ctx);
Petr Lautrbach bffd1c
 	authctxt->jpake_ctx = NULL;
Petr Lautrbach bffd1c
-	userauth_finish(authctxt, authenticated, method_jpake.name);
Petr Lautrbach bffd1c
+	userauth_finish(authctxt, authenticated, method_jpake.name, NULL);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 #endif /* JPAKE */
Petr Lautrbach bffd1c
diff --git a/auth2.c b/auth2.c
Petr Lautrbach bffd1c
index b66bef6..ea0fd92 100644
Petr Lautrbach bffd1c
--- a/auth2.c
Petr Lautrbach bffd1c
+++ b/auth2.c
Petr Lautrbach bffd1c
@@ -96,8 +96,10 @@ static void input_service_request(int, u_int32_t, void *);
Petr Lautrbach bffd1c
 static void input_userauth_request(int, u_int32_t, void *);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 /* helper */
Petr Lautrbach bffd1c
-static Authmethod *authmethod_lookup(const char *);
Petr Lautrbach bffd1c
-static char *authmethods_get(void);
Petr Lautrbach bffd1c
+static Authmethod *authmethod_lookup(Authctxt *, const char *);
Petr Lautrbach bffd1c
+static char *authmethods_get(Authctxt *authctxt);
Petr Lautrbach bffd1c
+static int method_allowed(Authctxt *, const char *);
Petr Lautrbach bffd1c
+static int list_starts_with(const char *, const char *);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 char *
Petr Lautrbach bffd1c
 auth2_read_banner(void)
Petr Lautrbach bffd1c
@@ -255,6 +257,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 		if (use_privsep)
Petr Lautrbach bffd1c
 			mm_inform_authserv(service, style);
Petr Lautrbach bffd1c
 		userauth_banner();
Petr Lautrbach bffd1c
+		if (auth2_setup_methods_lists(authctxt) != 0)
Petr Lautrbach bffd1c
+			packet_disconnect("no authentication methods enabled");
Petr Lautrbach bffd1c
 	} else if (strcmp(user, authctxt->user) != 0 ||
Petr Lautrbach bffd1c
 	    strcmp(service, authctxt->service) != 0) {
Petr Lautrbach bffd1c
 		packet_disconnect("Change of username or service not allowed: "
Petr Lautrbach bffd1c
@@ -277,12 +281,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 	authctxt->server_caused_failure = 0;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	/* try to authenticate user */
Petr Lautrbach bffd1c
-	m = authmethod_lookup(method);
Petr Lautrbach bffd1c
+	m = authmethod_lookup(authctxt, method);
Petr Lautrbach bffd1c
 	if (m != NULL && authctxt->failures < options.max_authtries) {
Petr Lautrbach bffd1c
 		debug2("input_userauth_request: try method %s", method);
Petr Lautrbach bffd1c
 		authenticated =	m->userauth(authctxt);
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
-	userauth_finish(authctxt, authenticated, method);
Petr Lautrbach bffd1c
+	userauth_finish(authctxt, authenticated, method, NULL);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	xfree(service);
Petr Lautrbach bffd1c
 	xfree(user);
Petr Lautrbach bffd1c
@@ -290,13 +294,17 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 void
Petr Lautrbach bffd1c
-userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Petr Lautrbach bffd1c
+userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
Petr Lautrbach bffd1c
+    const char *submethod)
Petr Lautrbach bffd1c
 {
Petr Lautrbach bffd1c
 	char *methods;
Petr Lautrbach bffd1c
+	int partial = 0;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	if (!authctxt->valid && authenticated)
Petr Lautrbach bffd1c
 		fatal("INTERNAL ERROR: authenticated invalid user %s",
Petr Lautrbach bffd1c
 		    authctxt->user);
Petr Lautrbach bffd1c
+	if (authenticated && authctxt->postponed)
Petr Lautrbach bffd1c
+		fatal("INTERNAL ERROR: authenticated and postponed");
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	/* Special handling for root */
Petr Lautrbach bffd1c
 	if (authenticated && authctxt->pw->pw_uid == 0 &&
Petr Lautrbach bffd1c
@@ -307,6 +315,19 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Petr Lautrbach bffd1c
 #endif
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
+	if (authenticated && options.num_auth_methods != 0) {
Petr Lautrbach bffd1c
+		if (!auth2_update_methods_lists(authctxt, method)) {
Petr Lautrbach bffd1c
+			authenticated = 0;
Petr Lautrbach bffd1c
+			partial = 1;
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	/* Log before sending the reply */
Petr Lautrbach bffd1c
+	auth_log(authctxt, authenticated, partial, method, submethod, " ssh2");
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	if (authctxt->postponed)
Petr Lautrbach bffd1c
+		return;
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 #ifdef USE_PAM
Petr Lautrbach bffd1c
 	if (options.use_pam && authenticated) {
Petr Lautrbach bffd1c
 		if (!PRIVSEP(do_pam_account())) {
Petr Lautrbach bffd1c
@@ -325,17 +346,10 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Petr Lautrbach bffd1c
 #ifdef _UNICOS
Petr Lautrbach bffd1c
 	if (authenticated && cray_access_denied(authctxt->user)) {
Petr Lautrbach bffd1c
 		authenticated = 0;
Petr Lautrbach bffd1c
-		fatal("Access denied for user %s.",authctxt->user);
Petr Lautrbach bffd1c
+		fatal("Access denied for user %s.", authctxt->user);
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
 #endif /* _UNICOS */
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
-	/* Log before sending the reply */
Petr Lautrbach bffd1c
-	auth_log(authctxt, authenticated, method, " ssh2");
Petr Lautrbach bffd1c
-
Petr Lautrbach bffd1c
-	if (authctxt->postponed)
Petr Lautrbach bffd1c
-		return;
Petr Lautrbach bffd1c
-
Petr Lautrbach bffd1c
-	/* XXX todo: check if multiple auth methods are needed */
Petr Lautrbach bffd1c
 	if (authenticated == 1) {
Petr Lautrbach bffd1c
 		/* turn off userauth */
Petr Lautrbach bffd1c
 		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
Petr Lautrbach bffd1c
@@ -348,7 +362,8 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 		/* Allow initial try of "none" auth without failure penalty */
Petr Lautrbach bffd1c
 		if (!authctxt->server_caused_failure &&
Petr Lautrbach bffd1c
-		    (authctxt->attempt > 1 || strcmp(method, "none") != 0))
Petr Lautrbach bffd1c
+		    (authctxt->attempt > 1 || strcmp(method, "none") != 0) &&
Petr Lautrbach bffd1c
+		    partial == 0)
Petr Lautrbach bffd1c
 			authctxt->failures++;
Petr Lautrbach bffd1c
 		if (authctxt->failures >= options.max_authtries) {
Petr Lautrbach bffd1c
 #ifdef SSH_AUDIT_EVENTS
Petr Lautrbach bffd1c
@@ -356,34 +371,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
Petr Lautrbach bffd1c
 #endif
Petr Lautrbach bffd1c
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
-		methods = authmethods_get();
Petr Lautrbach bffd1c
+		methods = authmethods_get(authctxt);
Petr Lautrbach bffd1c
+		debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
Petr Lautrbach bffd1c
+		    partial, methods);
Petr Lautrbach bffd1c
 		packet_start(SSH2_MSG_USERAUTH_FAILURE);
Petr Lautrbach bffd1c
 		packet_put_cstring(methods);
Petr Lautrbach bffd1c
-		packet_put_char(0);	/* XXX partial success, unused */
Petr Lautrbach bffd1c
+		packet_put_char(partial);
Petr Lautrbach bffd1c
 		packet_send();
Petr Lautrbach bffd1c
 		packet_write_wait();
Petr Lautrbach bffd1c
 		xfree(methods);
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
+/*
Petr Lautrbach bffd1c
+ * Checks whether method is allowed by at least one AuthenticationMethods
Petr Lautrbach bffd1c
+ * methods list. Returns 1 if allowed, or no methods lists configured.
Petr Lautrbach bffd1c
+ * 0 otherwise.
Petr Lautrbach bffd1c
+ */
Petr Lautrbach bffd1c
+static int
Petr Lautrbach bffd1c
+method_allowed(Authctxt *authctxt, const char *method)
Petr Lautrbach bffd1c
+{
Petr Lautrbach bffd1c
+	u_int i;
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	/*
Petr Lautrbach bffd1c
+	 * NB. authctxt->num_auth_methods might be zero as a result of
Petr Lautrbach bffd1c
+	 * auth2_setup_methods_lists(), so check the configuration.
Petr Lautrbach bffd1c
+	 */
Petr Lautrbach bffd1c
+	if (options.num_auth_methods == 0)
Petr Lautrbach bffd1c
+		return 1;
Petr Lautrbach bffd1c
+	for (i = 0; i < authctxt->num_auth_methods; i++) {
Petr Lautrbach bffd1c
+		if (list_starts_with(authctxt->auth_methods[i], method))
Petr Lautrbach bffd1c
+			return 1;
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+	return 0;
Petr Lautrbach bffd1c
+}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 static char *
Petr Lautrbach bffd1c
-authmethods_get(void)
Petr Lautrbach bffd1c
+authmethods_get(Authctxt *authctxt)
Petr Lautrbach bffd1c
 {
Petr Lautrbach bffd1c
 	Buffer b;
Petr Lautrbach bffd1c
 	char *list;
Petr Lautrbach bffd1c
-	int i;
Petr Lautrbach bffd1c
+	u_int i;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	buffer_init(&b);
Petr Lautrbach bffd1c
 	for (i = 0; authmethods[i] != NULL; i++) {
Petr Lautrbach bffd1c
 		if (strcmp(authmethods[i]->name, "none") == 0)
Petr Lautrbach bffd1c
 			continue;
Petr Lautrbach bffd1c
-		if (authmethods[i]->enabled != NULL &&
Petr Lautrbach bffd1c
-		    *(authmethods[i]->enabled) != 0) {
Petr Lautrbach bffd1c
-			if (buffer_len(&b) > 0)
Petr Lautrbach bffd1c
-				buffer_append(&b, ",", 1);
Petr Lautrbach bffd1c
-			buffer_append(&b, authmethods[i]->name,
Petr Lautrbach bffd1c
-			    strlen(authmethods[i]->name));
Petr Lautrbach bffd1c
-		}
Petr Lautrbach bffd1c
+		if (authmethods[i]->enabled == NULL ||
Petr Lautrbach bffd1c
+		    *(authmethods[i]->enabled) == 0)
Petr Lautrbach bffd1c
+			continue;
Petr Lautrbach bffd1c
+		if (!method_allowed(authctxt, authmethods[i]->name))
Petr Lautrbach bffd1c
+			continue;
Petr Lautrbach bffd1c
+		if (buffer_len(&b) > 0)
Petr Lautrbach bffd1c
+			buffer_append(&b, ",", 1);
Petr Lautrbach bffd1c
+		buffer_append(&b, authmethods[i]->name,
Petr Lautrbach bffd1c
+		    strlen(authmethods[i]->name));
Petr Lautrbach bffd1c
 	}
Petr Lautrbach bffd1c
 	buffer_append(&b, "\0", 1);
Petr Lautrbach bffd1c
 	list = xstrdup(buffer_ptr(&b);;
Petr Lautrbach bffd1c
@@ -392,7 +434,7 @@ authmethods_get(void)
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 static Authmethod *
Petr Lautrbach bffd1c
-authmethod_lookup(const char *name)
Petr Lautrbach bffd1c
+authmethod_lookup(Authctxt *authctxt, const char *name)
Petr Lautrbach bffd1c
 {
Petr Lautrbach bffd1c
 	int i;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
@@ -400,10 +442,152 @@ authmethod_lookup(const char *name)
Petr Lautrbach bffd1c
 		for (i = 0; authmethods[i] != NULL; i++)
Petr Lautrbach bffd1c
 			if (authmethods[i]->enabled != NULL &&
Petr Lautrbach bffd1c
 			    *(authmethods[i]->enabled) != 0 &&
Petr Lautrbach bffd1c
-			    strcmp(name, authmethods[i]->name) == 0)
Petr Lautrbach bffd1c
+			    strcmp(name, authmethods[i]->name) == 0 &&
Petr Lautrbach bffd1c
+			    method_allowed(authctxt, authmethods[i]->name))
Petr Lautrbach bffd1c
 				return authmethods[i];
Petr Lautrbach bffd1c
 	debug2("Unrecognized authentication method name: %s",
Petr Lautrbach bffd1c
 	    name ? name : "NULL");
Petr Lautrbach bffd1c
 	return NULL;
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
+/*
Petr Lautrbach bffd1c
+ * Check a comma-separated list of methods for validity. Is need_enable is
Petr Lautrbach bffd1c
+ * non-zero, then also require that the methods are enabled.
Petr Lautrbach bffd1c
+ * Returns 0 on success or -1 if the methods list is invalid.
Petr Lautrbach bffd1c
+ */
Petr Lautrbach bffd1c
+int
Petr Lautrbach bffd1c
+auth2_methods_valid(const char *_methods, int need_enable)
Petr Lautrbach bffd1c
+{
Petr Lautrbach bffd1c
+	char *methods, *omethods, *method;
Petr Lautrbach bffd1c
+	u_int i, found;
Petr Lautrbach bffd1c
+	int ret = -1;
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	if (*_methods == '\0') {
Petr Lautrbach bffd1c
+		error("empty authentication method list");
Petr Lautrbach bffd1c
+		return -1;
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+	omethods = methods = xstrdup(_methods);
Petr Lautrbach bffd1c
+	while ((method = strsep(&methods, ",")) != NULL) {
Petr Lautrbach bffd1c
+		for (found = i = 0; !found && authmethods[i] != NULL; i++) {
Petr Lautrbach bffd1c
+			if (strcmp(method, authmethods[i]->name) != 0)
Petr Lautrbach bffd1c
+				continue;
Petr Lautrbach bffd1c
+			if (need_enable) {
Petr Lautrbach bffd1c
+				if (authmethods[i]->enabled == NULL ||
Petr Lautrbach bffd1c
+				    *(authmethods[i]->enabled) == 0) {
Petr Lautrbach bffd1c
+					error("Disabled method \"%s\" in "
Petr Lautrbach bffd1c
+					    "AuthenticationMethods list \"%s\"",
Petr Lautrbach bffd1c
+					    method, _methods);
Petr Lautrbach bffd1c
+					goto out;
Petr Lautrbach bffd1c
+				}
Petr Lautrbach bffd1c
+			}
Petr Lautrbach bffd1c
+			found = 1;
Petr Lautrbach bffd1c
+			break;
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+		if (!found) {
Petr Lautrbach bffd1c
+			error("Unknown authentication method \"%s\" in list",
Petr Lautrbach bffd1c
+			    method);
Petr Lautrbach bffd1c
+			goto out;
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+	ret = 0;
Petr Lautrbach bffd1c
+ out:
Petr Lautrbach bffd1c
+	free(omethods);
Petr Lautrbach bffd1c
+	return ret;
Petr Lautrbach bffd1c
+}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+/*
Petr Lautrbach bffd1c
+ * Prune the AuthenticationMethods supplied in the configuration, removing
Petr Lautrbach bffd1c
+ * any methods lists that include disabled methods. Note that this might
Petr Lautrbach bffd1c
+ * leave authctxt->num_auth_methods == 0, even when multiple required auth
Petr Lautrbach bffd1c
+ * has been requested. For this reason, all tests for whether multiple is
Petr Lautrbach bffd1c
+ * enabled should consult options.num_auth_methods directly.
Petr Lautrbach bffd1c
+ */
Petr Lautrbach bffd1c
+int
Petr Lautrbach bffd1c
+auth2_setup_methods_lists(Authctxt *authctxt)
Petr Lautrbach bffd1c
+{
Petr Lautrbach bffd1c
+	u_int i;
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	if (options.num_auth_methods == 0)
Petr Lautrbach bffd1c
+		return 0;
Petr Lautrbach bffd1c
+	debug3("%s: checking methods", __func__);
Petr Lautrbach bffd1c
+	authctxt->auth_methods = xcalloc(options.num_auth_methods,
Petr Lautrbach bffd1c
+	    sizeof(*authctxt->auth_methods));
Petr Lautrbach bffd1c
+	authctxt->num_auth_methods = 0;
Petr Lautrbach bffd1c
+	for (i = 0; i < options.num_auth_methods; i++) {
Petr Lautrbach bffd1c
+		if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
Petr Lautrbach bffd1c
+			logit("Authentication methods list \"%s\" contains "
Petr Lautrbach bffd1c
+			    "disabled method, skipping",
Petr Lautrbach bffd1c
+			    options.auth_methods[i]);
Petr Lautrbach bffd1c
+			continue;
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+		debug("authentication methods list %d: %s",
Petr Lautrbach bffd1c
+		    authctxt->num_auth_methods, options.auth_methods[i]);
Petr Lautrbach bffd1c
+		authctxt->auth_methods[authctxt->num_auth_methods++] =
Petr Lautrbach bffd1c
+		    xstrdup(options.auth_methods[i]);
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+	if (authctxt->num_auth_methods == 0) {
Petr Lautrbach bffd1c
+		error("No AuthenticationMethods left after eliminating "
Petr Lautrbach bffd1c
+		    "disabled methods");
Petr Lautrbach bffd1c
+		return -1;
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+	return 0;
Petr Lautrbach bffd1c
+}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+static int
Petr Lautrbach bffd1c
+list_starts_with(const char *methods, const char *method)
Petr Lautrbach bffd1c
+{
Petr Lautrbach bffd1c
+	size_t l = strlen(method);
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	if (strncmp(methods, method, l) != 0)
Petr Lautrbach bffd1c
+		return 0;
Petr Lautrbach bffd1c
+	if (methods[l] != ',' && methods[l] != '\0')
Petr Lautrbach bffd1c
+		return 0;
Petr Lautrbach bffd1c
+	return 1;
Petr Lautrbach bffd1c
+}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+/*
Petr Lautrbach bffd1c
+ * Remove method from the start of a comma-separated list of methods.
Petr Lautrbach bffd1c
+ * Returns 0 if the list of methods did not start with that method or 1
Petr Lautrbach bffd1c
+ * if it did.
Petr Lautrbach bffd1c
+ */
Petr Lautrbach bffd1c
+static int
Petr Lautrbach bffd1c
+remove_method(char **methods, const char *method)
Petr Lautrbach bffd1c
+{
Petr Lautrbach bffd1c
+	char *omethods = *methods;
Petr Lautrbach bffd1c
+	size_t l = strlen(method);
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	if (!list_starts_with(omethods, method))
Petr Lautrbach bffd1c
+		return 0;
Petr Lautrbach bffd1c
+	*methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
Petr Lautrbach bffd1c
+	free(omethods);
Petr Lautrbach bffd1c
+	return 1;
Petr Lautrbach bffd1c
+}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+/*
Petr Lautrbach bffd1c
+ * Called after successful authentication. Will remove the successful method
Petr Lautrbach bffd1c
+ * from the start of each list in which it occurs. If it was the last method
Petr Lautrbach bffd1c
+ * in any list, then authentication is deemed successful.
Petr Lautrbach bffd1c
+ * Returns 1 if the method completed any authentication list or 0 otherwise.
Petr Lautrbach bffd1c
+ */
Petr Lautrbach bffd1c
+int
Petr Lautrbach bffd1c
+auth2_update_methods_lists(Authctxt *authctxt, const char *method)
Petr Lautrbach bffd1c
+{
Petr Lautrbach bffd1c
+	u_int i, found = 0;
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	debug3("%s: updating methods list after \"%s\"", __func__, method);
Petr Lautrbach bffd1c
+	for (i = 0; i < authctxt->num_auth_methods; i++) {
Petr Lautrbach bffd1c
+		if (!remove_method(&(authctxt->auth_methods[i]), method))
Petr Lautrbach bffd1c
+			continue;
Petr Lautrbach bffd1c
+		found = 1;
Petr Lautrbach bffd1c
+		if (*authctxt->auth_methods[i] == '\0') {
Petr Lautrbach bffd1c
+			debug2("authentication methods list %d complete", i);
Petr Lautrbach bffd1c
+			return 1;
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+		debug3("authentication methods list %d remaining: \"%s\"",
Petr Lautrbach bffd1c
+		    i, authctxt->auth_methods[i]);
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+	/* This should not happen, but would be bad if it did */
Petr Lautrbach bffd1c
+	if (!found)
Petr Lautrbach bffd1c
+		fatal("%s: method not in AuthenticationMethods", __func__);
Petr Lautrbach bffd1c
+	return 0;
Petr Lautrbach bffd1c
+}
Petr Lautrbach bffd1c
diff --git a/monitor.c b/monitor.c
Petr Lautrbach bffd1c
index 1dc42f5..66f3eea 100644
Petr Lautrbach bffd1c
--- a/monitor.c
Petr Lautrbach bffd1c
+++ b/monitor.c
Petr Lautrbach bffd1c
@@ -199,6 +199,7 @@ static int key_blobtype = MM_NOKEY;
Petr Lautrbach bffd1c
 static char *hostbased_cuser = NULL;
Petr Lautrbach bffd1c
 static char *hostbased_chost = NULL;
Petr Lautrbach bffd1c
 static char *auth_method = "unknown";
Petr Lautrbach bffd1c
+static char *auth_submethod = NULL;
Petr Lautrbach bffd1c
 static u_int session_id2_len = 0;
Petr Lautrbach bffd1c
 static u_char *session_id2 = NULL;
Petr Lautrbach bffd1c
 static pid_t monitor_child_pid;
Petr Lautrbach bffd1c
@@ -352,7 +353,7 @@ void
Petr Lautrbach bffd1c
 monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
Petr Lautrbach bffd1c
 {
Petr Lautrbach bffd1c
 	struct mon_table *ent;
Petr Lautrbach bffd1c
-	int authenticated = 0;
Petr Lautrbach bffd1c
+	int authenticated = 0, partial = 0;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	debug3("preauth child monitor started");
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
@@ -379,8 +380,26 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	/* The first few requests do not require asynchronous access */
Petr Lautrbach bffd1c
 	while (!authenticated) {
Petr Lautrbach bffd1c
+		partial = 0;
Petr Lautrbach bffd1c
 		auth_method = "unknown";
Petr Lautrbach bffd1c
+		auth_submethod = NULL;
Petr Lautrbach bffd1c
 		authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+		/* Special handling for multiple required authentications */
Petr Lautrbach bffd1c
+		if (options.num_auth_methods != 0) {
Petr Lautrbach bffd1c
+			if (!compat20)
Petr Lautrbach bffd1c
+				fatal("AuthenticationMethods is not supported"
Petr Lautrbach bffd1c
+				    "with SSH protocol 1");
Petr Lautrbach bffd1c
+			if (authenticated &&
Petr Lautrbach bffd1c
+			    !auth2_update_methods_lists(authctxt,
Petr Lautrbach bffd1c
+			    auth_method)) {
Petr Lautrbach bffd1c
+				debug3("%s: method %s: partial", __func__,
Petr Lautrbach bffd1c
+				    auth_method);
Petr Lautrbach bffd1c
+				authenticated = 0;
Petr Lautrbach bffd1c
+				partial = 1;
Petr Lautrbach bffd1c
+			}
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 		if (authenticated) {
Petr Lautrbach bffd1c
 			if (!(ent->flags & MON_AUTHDECIDE))
Petr Lautrbach bffd1c
 				fatal("%s: unexpected authentication from %d",
Petr Lautrbach bffd1c
@@ -403,9 +422,10 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 		if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
Petr Lautrbach bffd1c
-			auth_log(authctxt, authenticated, auth_method,
Petr Lautrbach bffd1c
+			auth_log(authctxt, authenticated, partial,
Petr Lautrbach bffd1c
+			    auth_method, auth_submethod,
Petr Lautrbach bffd1c
 			    compat20 ? " ssh2" : "");
Petr Lautrbach bffd1c
-			if (!authenticated)
Petr Lautrbach bffd1c
+			if (!authenticated && !partial)
Petr Lautrbach bffd1c
 				authctxt->failures++;
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
 #ifdef JPAKE
Petr Lautrbach bffd1c
@@ -781,7 +801,17 @@ mm_answer_pwnamallow(int sock, Buffer *m)
Petr Lautrbach bffd1c
 	COPY_MATCH_STRING_OPTS();
Petr Lautrbach bffd1c
 #undef M_CP_STROPT
Petr Lautrbach bffd1c
 #undef M_CP_STRARRAYOPT
Petr Lautrbach bffd1c
-	
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	/* Create valid auth method lists */
Petr Lautrbach bffd1c
+	if (compat20 && auth2_setup_methods_lists(authctxt) != 0) {
Petr Lautrbach bffd1c
+		/*
Petr Lautrbach bffd1c
+		 * The monitor will continue long enough to let the child
Petr Lautrbach bffd1c
+		 * run to it's packet_disconnect(), but it must not allow any
Petr Lautrbach bffd1c
+		 * authentication to succeed.
Petr Lautrbach bffd1c
+		 */
Petr Lautrbach bffd1c
+		debug("%s: no valid authentication method lists", __func__);
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 	debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
Petr Lautrbach bffd1c
 	mm_request_send(sock, MONITOR_ANS_PWNAM, m);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
@@ -918,7 +948,11 @@ mm_answer_bsdauthrespond(int sock, Buffer *m)
Petr Lautrbach bffd1c
 	debug3("%s: sending authenticated: %d", __func__, authok);
Petr Lautrbach bffd1c
 	mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
-	auth_method = "bsdauth";
Petr Lautrbach bffd1c
+	if (compat20)
Petr Lautrbach bffd1c
+		auth_method = "keyboard-interactive"; /* XXX auth_submethod */
Petr Lautrbach bffd1c
+	else
Petr Lautrbach bffd1c
+		auth_method = "bsdauth";
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	return (authok != 0);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
@@ -1057,7 +1091,9 @@ mm_answer_pam_query(int sock, Buffer *m)
Petr Lautrbach bffd1c
 		xfree(prompts);
Petr Lautrbach bffd1c
 	if (echo_on != NULL)
Petr Lautrbach bffd1c
 		xfree(echo_on);
Petr Lautrbach bffd1c
-	auth_method = "keyboard-interactive/pam";
Petr Lautrbach bffd1c
+	auth_method = "keyboard-interactive";
Petr Lautrbach bffd1c
+	auth_submethod = "pam";
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 	mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m);
Petr Lautrbach bffd1c
 	return (0);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
@@ -1086,7 +1122,8 @@ mm_answer_pam_respond(int sock, Buffer *m)
Petr Lautrbach bffd1c
 	buffer_clear(m);
Petr Lautrbach bffd1c
 	buffer_put_int(m, ret);
Petr Lautrbach bffd1c
 	mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m);
Petr Lautrbach bffd1c
-	auth_method = "keyboard-interactive/pam";
Petr Lautrbach bffd1c
+	auth_method = "keyboard-interactive";
Petr Lautrbach bffd1c
+	auth_submethod= "pam";
Petr Lautrbach bffd1c
 	if (ret == 0)
Petr Lautrbach bffd1c
 		sshpam_authok = sshpam_ctxt;
Petr Lautrbach bffd1c
 	return (0);
Petr Lautrbach bffd1c
@@ -1100,7 +1137,8 @@ mm_answer_pam_free_ctx(int sock, Buffer *m)
Petr Lautrbach bffd1c
 	(sshpam_device.free_ctx)(sshpam_ctxt);
Petr Lautrbach bffd1c
 	buffer_clear(m);
Petr Lautrbach bffd1c
 	mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m);
Petr Lautrbach bffd1c
-	auth_method = "keyboard-interactive/pam";
Petr Lautrbach bffd1c
+	auth_method = "keyboard-interactive";
Petr Lautrbach bffd1c
+	auth_submethod = "pam";
Petr Lautrbach bffd1c
 	return (sshpam_authok == sshpam_ctxt);
Petr Lautrbach bffd1c
 }
Petr Lautrbach bffd1c
 #endif
Petr Lautrbach bffd1c
@@ -1178,7 +1216,8 @@ mm_answer_keyallowed(int sock, Buffer *m)
Petr Lautrbach bffd1c
 		hostbased_chost = chost;
Petr Lautrbach bffd1c
 	} else {
Petr Lautrbach bffd1c
 		/* Log failed attempt */
Petr Lautrbach bffd1c
-		auth_log(authctxt, 0, auth_method, compat20 ? " ssh2" : "");
Petr Lautrbach bffd1c
+		auth_log(authctxt, 0, 0, auth_method, NULL,
Petr Lautrbach bffd1c
+		    compat20 ? " ssh2" : "");
Petr Lautrbach bffd1c
 		xfree(blob);
Petr Lautrbach bffd1c
 		xfree(cuser);
Petr Lautrbach bffd1c
 		xfree(chost);
Petr Lautrbach bffd1c
diff --git a/servconf.c b/servconf.c
Petr Lautrbach bffd1c
index 906778f..2c84993 100644
Petr Lautrbach bffd1c
--- a/servconf.c
Petr Lautrbach bffd1c
+++ b/servconf.c
Petr Lautrbach bffd1c
@@ -48,6 +48,8 @@
Petr Lautrbach bffd1c
 #include "groupaccess.h"
Petr Lautrbach bffd1c
 #include "canohost.h"
Petr Lautrbach bffd1c
 #include "packet.h"
Petr Lautrbach bffd1c
+#include "hostfile.h"
Petr Lautrbach bffd1c
+#include "auth.h"
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 static void add_listen_addr(ServerOptions *, char *, int);
Petr Lautrbach bffd1c
 static void add_one_listen_addr(ServerOptions *, char *, int);
Petr Lautrbach bffd1c
@@ -329,6 +331,7 @@ typedef enum {
Petr Lautrbach bffd1c
 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
Petr Lautrbach bffd1c
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
Petr Lautrbach bffd1c
 	sKexAlgorithms, sIPQoS, sVersionAddendum,
Petr Lautrbach bffd1c
+	sAuthenticationMethods,
Petr Lautrbach bffd1c
 	sDeprecated, sUnsupported
Petr Lautrbach bffd1c
 } ServerOpCodes;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
@@ -454,6 +457,7 @@ static struct {
Petr Lautrbach bffd1c
 	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
Petr Lautrbach bffd1c
 	{ "ipqos", sIPQoS, SSHCFG_ALL },
Petr Lautrbach bffd1c
 	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
Petr Lautrbach bffd1c
+	{ "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
Petr Lautrbach bffd1c
 	{ NULL, sBadOption, 0 }
Petr Lautrbach bffd1c
 };
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
@@ -1498,6 +1502,24 @@ process_server_config_line(ServerOptions *options, char *line,
Petr Lautrbach bffd1c
 		}
Petr Lautrbach bffd1c
 		return 0;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
+	case sAuthenticationMethods:
Petr Lautrbach bffd1c
+		if (*activep && options->num_auth_methods == 0) {
Petr Lautrbach bffd1c
+			while ((arg = strdelim(&cp)) && *arg != '\0') {
Petr Lautrbach bffd1c
+				if (options->num_auth_methods >=
Petr Lautrbach bffd1c
+				    MAX_AUTH_METHODS)
Petr Lautrbach bffd1c
+					fatal("%s line %d: "
Petr Lautrbach bffd1c
+					    "too many authentication methods.",
Petr Lautrbach bffd1c
+					    filename, linenum);
Petr Lautrbach bffd1c
+				if (auth2_methods_valid(arg, 0) != 0)
Petr Lautrbach bffd1c
+					fatal("%s line %d: invalid "
Petr Lautrbach bffd1c
+					    "authentication method list.",
Petr Lautrbach bffd1c
+					    filename, linenum);
Petr Lautrbach bffd1c
+				options->auth_methods[
Petr Lautrbach bffd1c
+				    options->num_auth_methods++] = xstrdup(arg);
Petr Lautrbach bffd1c
+			}
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+		return 0;
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 	case sDeprecated:
Petr Lautrbach bffd1c
 		logit("%s line %d: Deprecated option %s",
Petr Lautrbach bffd1c
 		    filename, linenum, arg);
Petr Lautrbach bffd1c
@@ -1925,6 +1947,8 @@ dump_config(ServerOptions *o)
Petr Lautrbach bffd1c
 	dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups);
Petr Lautrbach bffd1c
 	dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups);
Petr Lautrbach bffd1c
 	dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env);
Petr Lautrbach bffd1c
+	dump_cfg_strarray_oneline(sAuthenticationMethods,
Petr Lautrbach bffd1c
+		o->num_auth_methods, o->auth_methods);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	/* other arguments */
Petr Lautrbach bffd1c
 	for (i = 0; i < o->num_subsystems; i++)
Petr Lautrbach bffd1c
diff --git a/servconf.h b/servconf.h
Petr Lautrbach bffd1c
index 096d596..ef80eef 100644
Petr Lautrbach bffd1c
--- a/servconf.h
Petr Lautrbach bffd1c
+++ b/servconf.h
Petr Lautrbach bffd1c
@@ -28,6 +28,7 @@
Petr Lautrbach bffd1c
 #define MAX_ACCEPT_ENV		256	/* Max # of env vars. */
Petr Lautrbach bffd1c
 #define MAX_MATCH_GROUPS	256	/* Max # of groups for Match. */
Petr Lautrbach bffd1c
 #define MAX_AUTHKEYS_FILES	256	/* Max # of authorized_keys files. */
Petr Lautrbach bffd1c
+#define MAX_AUTH_METHODS	256	/* Max # of AuthenticationMethods. */
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 /* permit_root_login */
Petr Lautrbach bffd1c
 #define	PERMIT_NOT_SET		-1
Petr Lautrbach bffd1c
@@ -168,6 +169,9 @@ typedef struct {
Petr Lautrbach bffd1c
 	char   *authorized_principals_file;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 	char   *version_addendum;	/* Appended to SSH banner */
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
+	u_int	num_auth_methods;
Petr Lautrbach bffd1c
+	char   *auth_methods[MAX_AUTH_METHODS];
Petr Lautrbach bffd1c
 }       ServerOptions;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 /* Information about the incoming connection as used by Match */
Petr Lautrbach bffd1c
@@ -197,6 +201,7 @@ struct connection_info {
Petr Lautrbach bffd1c
 		M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \
Petr Lautrbach bffd1c
 		M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \
Petr Lautrbach bffd1c
 		M_CP_STRARRAYOPT(accept_env, num_accept_env); \
Petr Lautrbach bffd1c
+		M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
Petr Lautrbach bffd1c
 	} while (0)
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
 struct connection_info *get_connection_info(int, int);
Petr Lautrbach bffd1c
diff --git a/sshd.c b/sshd.c
Petr Lautrbach bffd1c
index d5ec4e6..cb4bdd3 100644
Petr Lautrbach bffd1c
--- a/sshd.c
Petr Lautrbach bffd1c
+++ b/sshd.c
Petr Lautrbach bffd1c
@@ -1333,6 +1333,7 @@ main(int ac, char **av)
Petr Lautrbach bffd1c
 	int remote_port;
Petr Lautrbach bffd1c
 	char *line;
Petr Lautrbach bffd1c
 	int config_s[2] = { -1 , -1 };
Petr Lautrbach bffd1c
+	u_int n;
Petr Lautrbach bffd1c
 	u_int64_t ibytes, obytes;
Petr Lautrbach bffd1c
 	mode_t new_umask;
Petr Lautrbach bffd1c
 	Key *key;
Petr Lautrbach bffd1c
@@ -1555,6 +1556,26 @@ main(int ac, char **av)
Petr Lautrbach bffd1c
 	if (options.challenge_response_authentication)
Petr Lautrbach bffd1c
 		options.kbd_interactive_authentication = 1;
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
+	/*
Petr Lautrbach bffd1c
+	 * Check whether there is any path through configured auth methods.
Petr Lautrbach bffd1c
+	 * Unfortunately it is not possible to verify this generally before
Petr Lautrbach bffd1c
+	 * daemonisation in the presence of Match block, but this catches
Petr Lautrbach bffd1c
+	 * and warns for trivial misconfigurations that could break login.
Petr Lautrbach bffd1c
+	 */
Petr Lautrbach bffd1c
+	if (options.num_auth_methods != 0) {
Petr Lautrbach bffd1c
+		if ((options.protocol & SSH_PROTO_1))
Petr Lautrbach bffd1c
+			fatal("AuthenticationMethods is not supported with "
Petr Lautrbach bffd1c
+			    "SSH protocol 1");
Petr Lautrbach bffd1c
+		for (n = 0; n < options.num_auth_methods; n++) {
Petr Lautrbach bffd1c
+			if (auth2_methods_valid(options.auth_methods[n],
Petr Lautrbach bffd1c
+			    1) == 0)
Petr Lautrbach bffd1c
+				break;
Petr Lautrbach bffd1c
+		}
Petr Lautrbach bffd1c
+		if (n >= options.num_auth_methods)
Petr Lautrbach bffd1c
+			fatal("AuthenticationMethods cannot be satisfied by "
Petr Lautrbach bffd1c
+			    "enabled authentication methods");
Petr Lautrbach bffd1c
+	}
Petr Lautrbach bffd1c
+
Petr Lautrbach bffd1c
 	/* set default channel AF */
Petr Lautrbach bffd1c
 	channel_set_af(options.address_family);
Petr Lautrbach bffd1c
 
Petr Lautrbach bffd1c
diff --git a/sshd_config.5 b/sshd_config.5
Petr Lautrbach bffd1c
index 314ecfb..ed81ac8 100644
Petr Lautrbach bffd1c
--- a/sshd_config.5
Petr Lautrbach bffd1c
+++ b/sshd_config.5
Petr Lautrbach bffd1c
@@ -151,6 +151,28 @@ See
Petr Lautrbach bffd1c
 in
Petr Lautrbach bffd1c
 .Xr ssh_config 5
Petr Lautrbach bffd1c
 for more information on patterns.
Petr Lautrbach bffd1c
+.It Cm AuthenticationMethods
Petr Lautrbach bffd1c
+Specifies the authentication methods that must be successfully completed
Petr Lautrbach bffd1c
+for a user to be granted access.
Petr Lautrbach bffd1c
+This option must be followed by one or more comma-separated lists of
Petr Lautrbach bffd1c
+authentication method names.
Petr Lautrbach bffd1c
+Successful authentication requires completion of every method in at least
Petr Lautrbach bffd1c
+one of these lists.
Petr Lautrbach bffd1c
+.Pp
Petr Lautrbach bffd1c
+For example, an argument of
Petr Lautrbach bffd1c
+.Dq publickey,password publickey,keyboard-interactive
Petr Lautrbach bffd1c
+would require the user to complete public key authentication, followed by
Petr Lautrbach bffd1c
+either password or keyboard interactive authentication.
Petr Lautrbach bffd1c
+Only methods that are next in one or more lists are offered at each stage,
Petr Lautrbach bffd1c
+so for this example, it would not be possible to attempt password or
Petr Lautrbach bffd1c
+keyboard-interactive authentication before public key.
Petr Lautrbach bffd1c
+.Pp
Petr Lautrbach bffd1c
+This option is only available for SSH protocol 2 and will yield a fatal
Petr Lautrbach bffd1c
+error if enabled if protocol 1 is also enabled.
Petr Lautrbach bffd1c
+Note that each authentication method listed should also be explicitly enabled
Petr Lautrbach bffd1c
+in the configuration.
Petr Lautrbach bffd1c
+The default is not to require multiple authentication; successful completion
Petr Lautrbach bffd1c
+of a single authentication method is sufficient.
Petr Lautrbach bffd1c
 .It Cm AuthorizedKeysFile
Petr Lautrbach bffd1c
 Specifies the file that contains the public keys that can be used
Petr Lautrbach bffd1c
 for user authentication.
Petr Lautrbach bffd1c
@@ -711,6 +733,7 @@ Available keywords are
Petr Lautrbach bffd1c
 .Cm AllowGroups ,
Petr Lautrbach bffd1c
 .Cm AllowTcpForwarding ,
Petr Lautrbach bffd1c
 .Cm AllowUsers ,
Petr Lautrbach bffd1c
+.Cm AuthenticationMethods ,
Petr Lautrbach bffd1c
 .Cm AuthorizedKeysFile ,
Petr Lautrbach bffd1c
 .Cm AuthorizedPrincipalsFile ,
Petr Lautrbach bffd1c
 .Cm Banner ,