--- openssh-3.1p1/auth.h.multipleauth	Wed Apr  7 11:34:32 2004
+++ openssh-3.1p1/auth.h	Wed Apr  7 11:34:15 2004
@@ -51,6 +51,7 @@
 	int		 valid;
 	int		 attempt;
 	int		 failures;
+	int		 passed;
 	char		*user;
 	char		*service;
 	struct passwd	*pw;
--- openssh-3.1p1/auth2.c.multipleauth	Wed Apr  7 12:55:00 2004
+++ openssh-3.1p1/auth2.c	Wed Apr  7 13:42:46 2004
@@ -74,7 +74,7 @@
 
 /* helper */
 static Authmethod *authmethod_lookup(const char *);
-static char *authmethods_get(void);
+static char *authmethods_get(int);
 static int user_key_allowed(struct passwd *, Key *);
 static int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
 
@@ -229,6 +229,7 @@
 userauth_finish(Authctxt *authctxt, int authenticated, char *method)
 {
 	char *methods;
+	int success = 0;
 
 	if (!authctxt->valid && authenticated)
 		fatal("INTERNAL ERROR: authenticated invalid user %s",
@@ -251,15 +252,22 @@
 	if (authctxt->postponed)
 		return;
 
-	/* XXX todo: check if multiple auth methods are needed */
+	/* Check if enough auth methods have passed */
 	if (authenticated == 1) {
-		/* turn off userauth */
-		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
-		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
-		packet_send();
-		packet_write_wait();
-		/* now we can break out */
-		authctxt->success = 1;
+		Authmethod *a;
+		int passed;
+		int k;
+
+		for (a = authmethods, k = 1, passed = 0; a->name != NULL; a++, k <<= 1) {
+			if (strncmp (method, a->name, strlen (a->name)) == 0)
+				authctxt->passed |= k;
+			if (authctxt->passed & k)
+				++passed;
+		}
+		if (passed < options.num_required_auth_methods) {
+			success = 1;
+			authenticated = 0;
+		}
 	} else {
 		if (authctxt->failures++ > AUTH_FAIL_MAX) {
 #ifdef WITH_AIXAUTHENTICATE
@@ -269,10 +277,21 @@
 #endif /* WITH_AIXAUTHENTICATE */
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
-		methods = authmethods_get();
+	}
+
+	if (authenticated == 1) {
+		/* turn off userauth */
+		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
+		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
+		packet_send();
+		packet_write_wait();
+		/* now we can break out */
+		authctxt->success = 1;
+	} else {
+		methods = authmethods_get(authctxt->passed);
 		packet_start(SSH2_MSG_USERAUTH_FAILURE);
 		packet_put_cstring(methods);
-		packet_put_char(0);	/* XXX partial success, unused */
+		packet_put_char(success);
 		packet_send();
 		packet_write_wait();
 		xfree(methods);
@@ -599,16 +618,19 @@
 #define	DELIM	","
 
 static char *
-authmethods_get(void)
+authmethods_get(int passed)
 {
 	Authmethod *method = NULL;
 	Buffer b;
 	char *list;
+	int k;
 
 	buffer_init(&b);
-	for (method = authmethods; method->name != NULL; method++) {
+	for (method = authmethods, k = 1; method->name != NULL; method++, k <<= 1) {
 		if (strcmp(method->name, "none") == 0)
 			continue;
+		if (passed & k)
+			continue;
 		if (method->enabled != NULL && *(method->enabled) != 0) {
 			if (buffer_len(&b) > 0)
 				buffer_append(&b, ",", 1);
--- openssh-3.1p1/servconf.h.multipleauth	Tue Mar  5 01:53:05 2002
+++ openssh-3.1p1/servconf.h	Wed Apr  7 12:53:38 2004
@@ -95,6 +95,8 @@
 						 * authentication. */
 	int     kbd_interactive_authentication;	/* If true, permit */
 	int     challenge_response_authentication;
+	int     num_required_auth_methods;	/* Minimum number of auth methods
+						 * that must succeed. */
 	int     permit_empty_passwd;	/* If false, do not permit empty
 					 * passwords. */
 	int     use_login;	/* If true, login(1) is used */
--- openssh-3.1p1/servconf.c.multipleauth	Tue Feb  5 01:26:35 2002
+++ openssh-3.1p1/servconf.c	Wed Apr  7 11:42:21 2004
@@ -89,6 +89,7 @@
 	options->password_authentication = -1;
 	options->kbd_interactive_authentication = -1;
 	options->challenge_response_authentication = -1;
+	options->num_required_auth_methods = -1;
 	options->permit_empty_passwd = -1;
 	options->use_login = -1;
 	options->allow_tcp_forwarding = -1;
@@ -206,6 +207,8 @@
 		options->kbd_interactive_authentication = 0;
 	if (options->challenge_response_authentication == -1)
 		options->challenge_response_authentication = 1;
+	if (options->num_required_auth_methods == -1)
+		options->num_required_auth_methods = 1;
 	if (options->permit_empty_passwd == -1)
 		options->permit_empty_passwd = 0;
 	if (options->use_login == -1)
@@ -255,6 +258,7 @@
 #ifdef AFS
 	sAFSTokenPassing,
 #endif
+	sNumRequiredAuthMethods,
 	sChallengeResponseAuthentication,
 	sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress,
 	sPrintMotd, sPrintLastLog, sIgnoreRhosts,
@@ -310,6 +314,7 @@
 	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
 	{ "challengeresponseauthentication", sChallengeResponseAuthentication },
 	{ "skeyauthentication", sChallengeResponseAuthentication }, /* alias */
+	{ "numrequiredauthmethods", sNumRequiredAuthMethods },
 	{ "checkmail", sDeprecated },
 	{ "listenaddress", sListenAddress },
 	{ "printmotd", sPrintMotd },
@@ -644,6 +649,10 @@
 		intptr = &options->challenge_response_authentication;
 		goto parse_flag;
 
+	case sNumRequiredAuthMethods:
+		intptr = &options->num_required_auth_methods;
+		goto parse_int;
+
 	case sPrintMotd:
 		intptr = &options->print_motd;
 		goto parse_flag;
--- openssh-3.1p1/sshd.8.multipleauth	Tue Mar  5 01:38:59 2002
+++ openssh-3.1p1/sshd.8	Wed Apr  7 12:37:34 2004
@@ -680,6 +680,12 @@
 are refused if the number of unauthenticated connections reaches
 .Dq full
 (60).
+.It Cm NumRequiredAuthMethods
+Specifies how many authentication methods must succeed during ssh2
+authentication. There are four potential methods: publickey, password,
+keyboard-interactive, and hostbased. Setting this value to 2 or higher forces
+the client to successfully authenticate in multiple ways, for example, using
+both S/Key and publickey.
 .It Cm PAMAuthenticationViaKbdInt
 Specifies whether PAM challenge response authentication is allowed. This
 allows the use of most PAM challenge response authentication modules, but 
--- openssh-3.1p1/sshd_config.multipleauth	Wed Apr  7 00:20:43 2004
+++ openssh-3.1p1/sshd_config	Wed Apr  7 12:39:23 2004
@@ -60,6 +60,10 @@
 # Change to no to disable s/key passwords
 #ChallengeResponseAuthentication yes
 
+# Change to require multiple authentication types, e.g. password and
+# publickey.
+#NumRequiredAuthMethods 1
+
 # Kerberos options
 # KerberosAuthentication automatically enabled if keyfile exists
 #KerberosAuthentication yes
