Blob Blame History Raw
diff -up openssh-5.9p1/auth.c.required-authentication openssh-5.9p1/auth.c
--- openssh-5.9p1/auth.c.required-authentication	2012-07-27 12:21:41.181601972 +0200
+++ openssh-5.9p1/auth.c	2012-07-27 12:21:41.203602020 +0200
@@ -251,7 +251,8 @@ allowed_user(struct passwd * pw)
 }
 
 void
-auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
+auth_log(Authctxt *authctxt, int authenticated, const char *method,
+    const char *submethod, const char *info)
 {
 	void (*authlog) (const char *fmt,...) = verbose;
 	char *authmsg;
@@ -271,9 +272,10 @@ auth_log(Authctxt *authctxt, int authent
 	else
 		authmsg = authenticated ? "Accepted" : "Failed";
 
-	authlog("%s %s for %s%.100s from %.200s port %d%s",
+	authlog("%s %s%s%s for %s%.100s from %.200s port %d%s",
 	    authmsg,
 	    method,
+	    submethod == NULL ? "" : "/", submethod == NULL ? "" : submethod,
 	    authctxt->valid ? "" : "invalid user ",
 	    authctxt->user,
 	    get_remote_ipaddr(),
@@ -303,7 +305,7 @@ auth_log(Authctxt *authctxt, int authent
  * Check whether root logins are disallowed.
  */
 int
-auth_root_allowed(char *method)
+auth_root_allowed(const char *method)
 {
 	switch (options.permit_root_login) {
 	case PERMIT_YES:
@@ -694,3 +696,57 @@ fakepw(void)
 
 	return (&fake);
 }
+
+int
+auth_method_in_list(const char *list, const char *method)
+{
+	char *cp;
+
+	cp = match_list(method, list, NULL);
+	if (cp != NULL) {
+		xfree(cp);
+		return 1;
+	}
+
+	return 0;
+}
+
+#define	DELIM	","
+int
+auth_remove_from_list(char **list, const char *method)
+{
+	char *oldlist, *cp, *newlist = NULL;
+	u_int len = 0, ret = 0;
+
+	if (list == NULL || *list == NULL)
+		return (0);
+
+	oldlist = *list;
+	len = strlen(oldlist) + 1;
+	newlist = xmalloc(len);
+	memset(newlist, '\0', len);
+
+	/* Remove method from list, if present */
+	for (;;) {
+		if ((cp = strsep(&oldlist, DELIM)) == NULL)
+			break;
+		if (*cp == '\0')
+			continue;
+		if (strcmp(cp, method) != 0) {
+			if (*newlist != '\0')
+				strlcat(newlist, DELIM, len);
+			strlcat(newlist, cp, len);
+		} else
+			ret++;
+	}
+
+	/* Return NULL instead of empty list */
+	if (*newlist == '\0') {
+		xfree(newlist);
+		newlist = NULL;
+	}
+	xfree(*list);
+	*list = newlist;
+	
+	return (ret);
+}
diff -up openssh-5.9p1/auth.h.required-authentication openssh-5.9p1/auth.h
--- openssh-5.9p1/auth.h.required-authentication	2011-05-29 13:39:38.000000000 +0200
+++ openssh-5.9p1/auth.h	2012-07-27 12:21:41.204602022 +0200
@@ -142,10 +142,11 @@ void disable_forwarding(void);
 void	do_authentication(Authctxt *);
 void	do_authentication2(Authctxt *);
 
-void	auth_log(Authctxt *, int, char *, char *);
-void	userauth_finish(Authctxt *, int, char *);
+void	auth_log(Authctxt *, int, const char *, const char *, const char *);
+void	userauth_finish(Authctxt *, int, const char *, const char *);
+int	auth_root_allowed(const char *);
+
 void	userauth_send_banner(const char *);
-int	auth_root_allowed(char *);
 
 char	*auth2_read_banner(void);
 
@@ -192,6 +193,11 @@ void	 auth_debug_send(void);
 void	 auth_debug_reset(void);
 
 struct passwd *fakepw(void);
+int	 auth_method_in_list(const char *, const char *);
+int	 auth_remove_from_list(char **, const char *);
+
+int	 auth1_check_required(const char *);
+int	 auth2_check_required(const char *);
 
 int	 sys_auth_passwd(Authctxt *, const char *);
 
diff -up openssh-5.9p1/auth1.c.required-authentication openssh-5.9p1/auth1.c
--- openssh-5.9p1/auth1.c.required-authentication	2010-08-31 14:36:39.000000000 +0200
+++ openssh-5.9p1/auth1.c	2012-07-27 12:50:50.708706675 +0200
@@ -98,6 +98,55 @@ static const struct AuthMethod1
 	return (NULL);
 }
 
+static const struct AuthMethod1 *
+lookup_authmethod1_by_name(const char *name)
+{
+	int i;
+
+	for (i = 0; auth1_methods[i].name != NULL; i++)
+		if (strcmp(auth1_methods[i].name, name) == 0)
+			return (&(auth1_methods[i]));
+
+	return NULL;
+}
+
+#define	DELIM	","
+int
+auth1_check_required(const char *list)
+{
+	char *orig_methods, *methods, *cp;
+	static const struct AuthMethod1 *m;
+	int ret = 0;
+
+	orig_methods = methods = xstrdup(list);
+	for(;;) { /* XXX maybe: while ((cp = ...) != NULL) ? */
+		if ((cp = strsep(&methods, DELIM)) == NULL)
+			break;
+		debug2("auth1_check_required: method \"%s\"", cp);
+		if (*cp == '\0') {
+			debug("auth1_check_required: empty method");
+			ret = -1;
+		}
+		if ((m = lookup_authmethod1_by_name(cp)) == NULL) {
+			debug("auth1_check_required: unknown method "
+			    "\"%s\"", cp);
+			ret = -1;
+			break;
+		}
+		if (*(m->enabled) == 0) {
+			debug("auth1_check_required: method %s explicitly "
+			    "disabled", cp);
+			ret = -1;
+		}
+		/* Activate method if it isn't already */
+		if (*(m->enabled) == -1)
+			*(m->enabled) = 1;
+        }
+	xfree(orig_methods);
+	return (ret);
+}
+
+
 static char *
 get_authname(int type)
 {
@@ -237,6 +286,7 @@ do_authloop(Authctxt *authctxt)
 {
 	int authenticated = 0;
 	char info[1024];
+	const char *meth_name;
 	int prev = 0, type = 0;
 	const struct AuthMethod1 *meth;
 
@@ -244,7 +294,7 @@ do_authloop(Authctxt *authctxt)
 	    authctxt->valid ? "" : "invalid user ", authctxt->user);
 
 	/* If the user has no password, accept authentication immediately. */
-	if (options.permit_empty_passwd && options.password_authentication &&
+	if (options.permit_empty_passwd && options.password_authentication && options.password_authentication &&
 #ifdef KRB5
 	    (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
 #endif
@@ -253,7 +303,7 @@ do_authloop(Authctxt *authctxt)
 		if (options.use_pam && (PRIVSEP(do_pam_account())))
 #endif
 		{
-			auth_log(authctxt, 1, "without authentication", "");
+			auth_log(authctxt, 1, "without authentication", NULL, "");
 			return;
 		}
 	}
@@ -272,6 +322,7 @@ do_authloop(Authctxt *authctxt)
 		/* Get a packet from the client. */
 		prev = type;
 		type = packet_read();
+		meth_name = get_authname(type);
 
 		/*
 		 * If we started challenge-response authentication but the
@@ -287,8 +338,8 @@ do_authloop(Authctxt *authctxt)
 		if (authctxt->failures >= options.max_authtries)
 			goto skip;
 		if ((meth = lookup_authmethod1(type)) == NULL) {
-			logit("Unknown message during authentication: "
-			    "type %d", type);
+			logit("Unknown message during authentication: type %d",
+			    type);
 			goto skip;
 		}
 
@@ -297,6 +348,17 @@ do_authloop(Authctxt *authctxt)
 			goto skip;
 		}
 
+		/*
+		 * Skip methods not in required list, until all the required
+		 * ones are done
+		 */
+		if (options.required_auth1 != NULL &&
+		    !auth_method_in_list(options.required_auth1, meth_name)) {
+			debug("Skipping method \"%s\" until required "
+			    "authentication completed", meth_name);
+			goto skip;
+		}
+
 		authenticated = meth->method(authctxt, info, sizeof(info));
 		if (authenticated == -1)
 			continue; /* "postponed" */
@@ -352,7 +414,29 @@ do_authloop(Authctxt *authctxt)
 
  skip:
 		/* Log before sending the reply */
-		auth_log(authctxt, authenticated, get_authname(type), info);
+		auth_log(authctxt, authenticated, meth_name, NULL, info);
+
+		/* Loop until the required authmethods are done */
+		if (authenticated && options.required_auth1 != NULL) {
+			if (auth_remove_from_list(&options.required_auth1,
+			    meth_name) == 0)
+				fatal("INTERNAL ERROR: authenticated method "
+				    "\"%s\" not in required list \"%s\"",
+				    meth_name, options.required_auth1);
+			debug2("do_authloop: required list now: %s",
+			    options.required_auth1 == NULL ?
+			    "DONE" : options.required_auth1);
+			if (options.required_auth1 == NULL)
+				return;
+			authenticated = 0;
+			/*
+			 * Disable method so client can't authenticate with it
+			 * after the required authentications are complete.
+			 */
+			*(meth->enabled) = 0;
+			packet_send_debug("Further authentication required");
+			goto send_fail;
+		}
 
 		if (client_user != NULL) {
 			xfree(client_user);
@@ -368,6 +452,7 @@ do_authloop(Authctxt *authctxt)
 #endif
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
+ send_fail:
 
 		packet_start(SSH_SMSG_FAILURE);
 		packet_send();
diff -up openssh-5.9p1/auth2.c.required-authentication openssh-5.9p1/auth2.c
--- openssh-5.9p1/auth2.c.required-authentication	2011-05-05 06:04:11.000000000 +0200
+++ openssh-5.9p1/auth2.c	2012-07-27 12:51:59.048241612 +0200
@@ -215,7 +215,7 @@ input_userauth_request(int type, u_int32
 {
 	Authctxt *authctxt = ctxt;
 	Authmethod *m = NULL;
-	char *user, *service, *method, *style = NULL;
+	char *user, *service, *method, *active_methods, *style = NULL;
 	int authenticated = 0;
 
 	if (authctxt == NULL)
@@ -277,22 +277,31 @@ input_userauth_request(int type, u_int32
 	authctxt->server_caused_failure = 0;
 
 	/* try to authenticate user */
-	m = authmethod_lookup(method);
-	if (m != NULL && authctxt->failures < options.max_authtries) {
-		debug2("input_userauth_request: try method %s", method);
-		authenticated =	m->userauth(authctxt);
-	}
-	userauth_finish(authctxt, authenticated, method);
+	active_methods = authmethods_get();
+	if (strcmp(method, "none") == 0 || 
+	    auth_method_in_list(active_methods, method)) {
+		m = authmethod_lookup(method);
+		if (m != NULL) {
+			debug2("input_userauth_request: try method %s", method);
+			authenticated =	m->userauth(authctxt);
+		}
 
+	}
+	xfree(active_methods);
+	userauth_finish(authctxt, authenticated, method, NULL);
+ 
 	xfree(service);
 	xfree(user);
 	xfree(method);
 }
 
 void
-userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
+    const char *submethod)
 {
 	char *methods;
+	Authmethod *m = NULL;
+	u_int partial = 0;
 
 	if (!authctxt->valid && authenticated)
 		fatal("INTERNAL ERROR: authenticated invalid user %s",
@@ -330,12 +339,42 @@ userauth_finish(Authctxt *authctxt, int
 #endif /* _UNICOS */
 
 	/* Log before sending the reply */
-	auth_log(authctxt, authenticated, method, " ssh2");
+	auth_log(authctxt, authenticated, method, submethod, " ssh2");
 
 	if (authctxt->postponed)
 		return;
 
-	/* XXX todo: check if multiple auth methods are needed */
+	/* Handle RequiredAuthentications2: loop until required methods done */
+	if (authenticated && options.required_auth2 != NULL) {
+		if ((m = authmethod_lookup(method)) == NULL)
+			fatal("INTERNAL ERROR: authenticated method "
+			    "\"%s\" unknown", method);
+		if (auth_remove_from_list(&options.required_auth2, method) == 0)
+			fatal("INTERNAL ERROR: authenticated method "
+			    "\"%s\" not in required list \"%s\"", 
+			    method, options.required_auth2);
+		debug2("userauth_finish: required list now: %s",
+		    options.required_auth2 == NULL ?
+		    "DONE" : options.required_auth2);
+		/*
+		 * if authenticated and no more required methods 
+		 * then declare success
+		 */
+		if ( authenticated && options.required_auth2 == NULL ) {
+			debug2("userauth_finish: authenticated and no more required methods");
+		} else {
+			/*
+			 * Disable method so client can't authenticate with it after
+			 * the required authentications are complete.
+			 */
+			if (m->enabled != NULL)
+			*(m->enabled) = 0;
+			authenticated = 0;
+			partial = 1;
+			goto send_fail;
+		}
+	}
+
 	if (authenticated == 1) {
 		/* turn off userauth */
 		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
@@ -345,7 +384,6 @@ userauth_finish(Authctxt *authctxt, int
 		/* now we can break out */
 		authctxt->success = 1;
 	} else {
-
 		/* Allow initial try of "none" auth without failure penalty */
 		if (!authctxt->server_caused_failure &&
 		    (authctxt->attempt > 1 || strcmp(method, "none") != 0))
@@ -356,10 +394,11 @@ userauth_finish(Authctxt *authctxt, int
 #endif
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
+ send_fail:
 		methods = authmethods_get();
 		packet_start(SSH2_MSG_USERAUTH_FAILURE);
 		packet_put_cstring(methods);
-		packet_put_char(0);	/* XXX partial success, unused */
+		packet_put_char(partial);
 		packet_send();
 		packet_write_wait();
 		xfree(methods);
@@ -373,6 +412,9 @@ authmethods_get(void)
 	char *list;
 	int i;
 
+	if (options.required_auth2 != NULL)
+		return xstrdup(options.required_auth2);
+
 	buffer_init(&b);
 	for (i = 0; authmethods[i] != NULL; i++) {
 		if (strcmp(authmethods[i]->name, "none") == 0)
@@ -407,3 +449,43 @@ authmethod_lookup(const char *name)
 	return NULL;
 }
 
+#define DELIM ","
+
+int
+auth2_check_required(const char *list)
+{
+	char *orig_methods, *methods, *cp;
+	struct Authmethod *m;
+	int i, ret = 0;
+
+	orig_methods = methods = xstrdup(list);
+	for(;;) {
+		if ((cp = strsep(&methods, DELIM)) == NULL)
+			break;
+		debug2("auth2_check_required: method \"%s\"", cp);
+		if (*cp == '\0') {
+			debug("auth2_check_required: empty method");
+			ret = -1;
+		}
+		for (i = 0; authmethods[i] != NULL; i++)
+			if (strcmp(cp, authmethods[i]->name) == 0)
+				break;
+		if ((m = authmethods[i]) == NULL) {
+			debug("auth2_check_required: unknown method "
+			    "\"%s\"", cp);
+			ret = -1;
+			break;
+		}
+		if (m->enabled == NULL || *(m->enabled) == 0) {
+			debug("auth2_check_required: method %s explicitly "
+			    "disabled", cp);
+			ret = -1;
+		}
+		/* Activate method if it isn't already */
+		if (m->enabled != NULL && *(m->enabled) == -1)
+			*(m->enabled) = 1;
+	}
+	xfree(orig_methods);
+	return (ret);
+}
+
diff -up openssh-5.9p1/auth2-gss.c.required-authentication openssh-5.9p1/auth2-gss.c
--- openssh-5.9p1/auth2-gss.c.required-authentication	2011-05-05 06:04:11.000000000 +0200
+++ openssh-5.9p1/auth2-gss.c	2012-07-27 12:21:41.206602026 +0200
@@ -163,7 +163,7 @@ input_gssapi_token(int type, u_int32_t p
 		}
 		authctxt->postponed = 0;
 		dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
-		userauth_finish(authctxt, 0, "gssapi-with-mic");
+		userauth_finish(authctxt, 0, "gssapi-with-mic", NULL);
 	} else {
 		if (send_tok.length != 0) {
 			packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
@@ -251,7 +251,7 @@ input_gssapi_exchange_complete(int type,
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
-	userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+	userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
 }
 
 static void
@@ -291,7 +291,7 @@ input_gssapi_mic(int type, u_int32_t ple
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
-	userauth_finish(authctxt, authenticated, "gssapi-with-mic");
+	userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL);
 }
 
 Authmethod method_gssapi = {
diff -up openssh-5.9p1/auth2-chall.c.required-authentication openssh-5.9p1/auth2-chall.c
--- openssh-5.9p1/auth2-chall.c.required-authentication	2009-01-28 06:13:39.000000000 +0100
+++ openssh-5.9p1/auth2-chall.c	2012-07-27 12:21:41.206602026 +0200
@@ -341,7 +341,8 @@ input_userauth_info_response(int type, u
 			auth2_challenge_start(authctxt);
 		}
 	}
-	userauth_finish(authctxt, authenticated, method);
+	userauth_finish(authctxt, authenticated, "keyboard-interactive",
+	    authctxt->kbdintctxt?kbdintctxt->device->name:NULL);
 	xfree(method);
 }
 
diff -up openssh-5.9p1/auth2-none.c.required-authentication openssh-5.9p1/auth2-none.c
--- openssh-5.9p1/auth2-none.c.required-authentication	2010-06-26 02:01:33.000000000 +0200
+++ openssh-5.9p1/auth2-none.c	2012-07-27 12:21:41.207602028 +0200
@@ -61,7 +61,7 @@ userauth_none(Authctxt *authctxt)
 {
 	none_enabled = 0;
 	packet_check_eom();
-	if (options.permit_empty_passwd && options.password_authentication)
+	if (options.permit_empty_passwd && options.password_authentication && options.required_auth2 == NULL)
 		return (PRIVSEP(auth_password(authctxt, "")));
 	return (0);
 }
diff -up openssh-5.9p1/monitor.c.required-authentication openssh-5.9p1/monitor.c
--- openssh-5.9p1/monitor.c.required-authentication	2012-07-27 12:21:41.161601930 +0200
+++ openssh-5.9p1/monitor.c	2012-07-27 12:51:18.884927066 +0200
@@ -199,6 +199,7 @@ static int key_blobtype = MM_NOKEY;
 static char *hostbased_cuser = NULL;
 static char *hostbased_chost = NULL;
 static char *auth_method = "unknown";
+static char *auth_submethod = NULL;
 static u_int session_id2_len = 0;
 static u_char *session_id2 = NULL;
 static pid_t monitor_child_pid;
@@ -353,6 +354,7 @@ monitor_child_preauth(Authctxt *_authctx
 {
 	struct mon_table *ent;
 	int authenticated = 0;
+	char **req_auth;
 
 	debug3("preauth child monitor started");
 
@@ -367,12 +369,14 @@ monitor_child_preauth(Authctxt *_authctx
 
 	if (compat20) {
 		mon_dispatch = mon_dispatch_proto20;
+		req_auth = &options.required_auth2;
 
 		/* Permit requests for moduli and signatures */
 		monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1);
 		monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
 	} else {
 		mon_dispatch = mon_dispatch_proto15;
+		req_auth = &options.required_auth1;
 
 		monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1);
 	}
@@ -380,6 +384,7 @@ monitor_child_preauth(Authctxt *_authctx
 	/* The first few requests do not require asynchronous access */
 	while (!authenticated) {
 		auth_method = "unknown";
+		auth_submethod = NULL;
 		authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1);
 		if (authenticated) {
 			if (!(ent->flags & MON_AUTHDECIDE))
@@ -401,10 +406,19 @@ monitor_child_preauth(Authctxt *_authctx
 			}
 #endif
 		}
+		/* Loop until the required authmethods are done */
+		if (authenticated && *req_auth != NULL) {
+			if (auth_remove_from_list(req_auth, auth_method) == 0)
+				fatal("INTERNAL ERROR: authenticated method "
+				    "\"%s\" not in required list \"%s\"",
+				    auth_method, *req_auth);
+			debug2("monitor_child_preauth: required list now: %s",
+			    *req_auth == NULL ? "DONE" : *req_auth);
+		}
 
 		if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
 			auth_log(authctxt, authenticated, auth_method,
-			    compat20 ? " ssh2" : "");
+				 auth_submethod, compat20 ? " ssh2" : "");
 			if (!authenticated)
 				authctxt->failures++;
 		}
@@ -417,6 +431,8 @@ monitor_child_preauth(Authctxt *_authctx
 			}
 		}
 #endif
+		if (*req_auth != NULL)
+			authenticated = 0;
 	}
 
 	/* Drain any buffered messages from the child */
@@ -862,6 +878,7 @@ mm_answer_authpassword(int sock, Buffer
 		auth_method = "none";
 	else
 		auth_method = "password";
+	auth_submethod = NULL;
 
 	/* Causes monitor loop to terminate if authenticated */
 	return (authenticated);
@@ -921,6 +938,7 @@ mm_answer_bsdauthrespond(int sock, Buffe
 	mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
 
 	auth_method = "bsdauth";
+	auth_submethod = NULL;
 
 	return (authok != 0);
 }
@@ -970,6 +988,7 @@ mm_answer_skeyrespond(int sock, Buffer *
 	mm_request_send(sock, MONITOR_ANS_SKEYRESPOND, m);
 
 	auth_method = "skey";
+	auth_submethod = NULL;
 
 	return (authok != 0);
 }
@@ -1059,7 +1078,8 @@ mm_answer_pam_query(int sock, Buffer *m)
 		xfree(prompts);
 	if (echo_on != NULL)
 		xfree(echo_on);
-	auth_method = "keyboard-interactive/pam";
+	auth_method = "keyboard-interactive";
+	auth_submethod = "pam";
 	mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m);
 	return (0);
 }
@@ -1088,7 +1108,8 @@ mm_answer_pam_respond(int sock, Buffer *
 	buffer_clear(m);
 	buffer_put_int(m, ret);
 	mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m);
-	auth_method = "keyboard-interactive/pam";
+	auth_method = "keyboard-interactive";
+	auth_submethod = "pam";
 	if (ret == 0)
 		sshpam_authok = sshpam_ctxt;
 	return (0);
@@ -1102,7 +1123,8 @@ mm_answer_pam_free_ctx(int sock, Buffer
 	(sshpam_device.free_ctx)(sshpam_ctxt);
 	buffer_clear(m);
 	mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m);
-	auth_method = "keyboard-interactive/pam";
+	auth_method = "keyboard-interactive";
+	auth_submethod = "pam";
 	return (sshpam_authok == sshpam_ctxt);
 }
 #endif
@@ -1138,6 +1160,7 @@ mm_answer_keyallowed(int sock, Buffer *m
 			allowed = options.pubkey_authentication &&
 			    user_key_allowed(authctxt->pw, key);
 			auth_method = "publickey";
+			auth_submethod = NULL;
 			if (options.pubkey_authentication && allowed != 1)
 				auth_clear_options();
 			break;
@@ -1146,6 +1169,7 @@ mm_answer_keyallowed(int sock, Buffer *m
 			    hostbased_key_allowed(authctxt->pw,
 			    cuser, chost, key);
 			auth_method = "hostbased";
+			auth_submethod = NULL;
 			break;
 		case MM_RSAHOSTKEY:
 			key->type = KEY_RSA1; /* XXX */
@@ -1155,6 +1179,7 @@ mm_answer_keyallowed(int sock, Buffer *m
 			if (options.rhosts_rsa_authentication && allowed != 1)
 				auth_clear_options();
 			auth_method = "rsa";
+			auth_submethod = NULL;
 			break;
 		default:
 			fatal("%s: unknown key type %d", __func__, type);
@@ -1180,7 +1205,8 @@ mm_answer_keyallowed(int sock, Buffer *m
 		hostbased_chost = chost;
 	} else {
 		/* Log failed attempt */
-		auth_log(authctxt, 0, auth_method, compat20 ? " ssh2" : "");
+		auth_log(authctxt, 0, auth_method, auth_submethod,
+		    compat20 ? " ssh2" : "");
 		xfree(blob);
 		xfree(cuser);
 		xfree(chost);
@@ -1356,6 +1382,7 @@ mm_answer_keyverify(int sock, Buffer *m)
 	xfree(data);
 
 	auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased";
+	auth_submethod = NULL;
 
 	monitor_reset_key_state();
 
@@ -1545,6 +1572,7 @@ mm_answer_rsa_keyallowed(int sock, Buffe
 	debug3("%s entering", __func__);
 
 	auth_method = "rsa";
+	auth_submethod = NULL;
 	if (options.rsa_authentication && authctxt->valid) {
 		if ((client_n = BN_new()) == NULL)
 			fatal("%s: BN_new", __func__);
@@ -1650,6 +1678,7 @@ mm_answer_rsa_response(int sock, Buffer
 	xfree(response);
 
 	auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa";
+	auth_submethod = NULL;
 
 	/* reset state */
 	BN_clear_free(ssh1_challenge);
@@ -2099,6 +2128,7 @@ mm_answer_gss_userok(int sock, Buffer *m
 	mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m);
 
 	auth_method = "gssapi-with-mic";
+	auth_submethod = NULL;
 
 	/* Monitor loop will terminate if authenticated */
 	return (authenticated);
@@ -2303,6 +2333,7 @@ mm_answer_jpake_check_confirm(int sock,
 	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1);
 
 	auth_method = "jpake-01@openssh.com";
+	auth_submethod = NULL;
 	return authenticated;
 }
 
diff -up openssh-5.9p1/servconf.c.required-authentication openssh-5.9p1/servconf.c
--- openssh-5.9p1/servconf.c.required-authentication	2012-07-27 12:21:41.167601942 +0200
+++ openssh-5.9p1/servconf.c	2012-07-27 12:21:41.209602032 +0200
@@ -42,6 +42,8 @@
 #include "key.h"
 #include "kex.h"
 #include "mac.h"
+#include "hostfile.h"
+#include "auth.h"
 #include "match.h"
 #include "channels.h"
 #include "groupaccess.h"
@@ -129,6 +131,8 @@ initialize_server_options(ServerOptions
 	options->num_authkeys_files = 0;
 	options->num_accept_env = 0;
 	options->permit_tun = -1;
+	options->required_auth1 = NULL;
+	options->required_auth2 = NULL;
 	options->num_permitted_opens = -1;
 	options->adm_forced_command = NULL;
 	options->chroot_directory = NULL;
@@ -319,6 +323,7 @@ typedef enum {
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
 	sClientAliveCountMax, sAuthorizedKeysFile,
 	sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
+	sRequiredAuthentications1, sRequiredAuthentications2,
 	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
@@ -447,6 +452,8 @@ static struct {
 	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
 	{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
 	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
+	{ "requiredauthentications1", sRequiredAuthentications1, SSHCFG_ALL },
+	{ "requiredauthentications2", sRequiredAuthentications2, SSHCFG_ALL },
 	{ "ipqos", sIPQoS, SSHCFG_ALL },
 	{ NULL, sBadOption, 0 }
 };
@@ -1220,6 +1227,33 @@ process_server_config_line(ServerOptions
 			options->max_startups = options->max_startups_begin;
 		break;
 
+
+	case sRequiredAuthentications1:
+		charptr = &options->required_auth1;
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing argument.",
+			    filename, linenum);
+		if (auth1_check_required(arg) != 0)
+			fatal("%.200s line %d: Invalid required authentication "
+			    "list", filename, linenum);
+		if (*charptr == NULL)
+			*charptr = xstrdup(arg);
+		break;
+
+	case sRequiredAuthentications2:
+		charptr = &options->required_auth2;
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing argument.",
+			    filename, linenum);
+		if (auth2_check_required(arg) != 0)
+			fatal("%.200s line %d: Invalid required authentication "
+			    "list", filename, linenum);
+		if (*charptr == NULL)
+			*charptr = xstrdup(arg);
+		break;
+
 	case sMaxAuthTries:
 		intptr = &options->max_authtries;
 		goto parse_int;
diff -up openssh-5.9p1/servconf.h.required-authentication openssh-5.9p1/servconf.h
--- openssh-5.9p1/servconf.h.required-authentication	2011-06-23 00:30:03.000000000 +0200
+++ openssh-5.9p1/servconf.h	2012-07-27 12:21:41.210602035 +0200
@@ -154,6 +154,9 @@ typedef struct {
 	u_int num_authkeys_files;	/* Files containing public keys */
 	char   *authorized_keys_files[MAX_AUTHKEYS_FILES];
 
+	char   *required_auth1; /* Required, but not sufficient */
+	char   *required_auth2;
+
 	char   *adm_forced_command;
 
 	int	use_pam;		/* Enable auth via PAM */
diff -up openssh-5.9p1/sshd_config.5.required-authentication openssh-5.9p1/sshd_config.5
--- openssh-5.9p1/sshd_config.5.required-authentication	2011-08-05 22:17:33.000000000 +0200
+++ openssh-5.9p1/sshd_config.5	2012-07-27 12:38:47.607222070 +0200
@@ -723,6 +723,8 @@ Available keywords are
 .Cm PermitOpen ,
 .Cm PermitRootLogin ,
 .Cm PermitTunnel ,
+.Cm RequiredAuthentications1,
+.Cm RequiredAuthentications2,
 .Cm PubkeyAuthentication ,
 .Cm RhostsRSAAuthentication ,
 .Cm RSAAuthentication ,
@@ -920,6 +922,21 @@ Specifies a list of revoked public keys.
 Keys listed in this file will be refused for public key authentication.
 Note that if this file is not readable, then public key authentication will
 be refused for all users.
+.It Cm RequiredAuthentications[12]
+ Specifies required methods of authentications that has to succeed before authorizing the connection.
+ (RequiredAuthentication1 for Protocol version 1, and RequiredAuthentication2 for v2)
+
+ RequiredAuthentications1 method[,method...] 
+ RequiredAuthentications2 method[,method...]
+
+.Pp
+Example 1:
+
+ RequiredAuthentications2 password,hostbased
+
+Example 2:
+ RequiredAuthentications2 publickey,password
+
 .It Cm RhostsRSAAuthentication
 Specifies whether rhosts or /etc/hosts.equiv authentication together
 with successful RSA host authentication is allowed.