--- auth.c.DIST	Tue Mar  5 02:42:43 2002
+++ auth.c	Thu May 23 14:50:25 2002
@@ -220,8 +220,15 @@
 
 	if (authctxt->postponed)
 		authmsg = "Postponed";
-	else
-		authmsg = authenticated ? "Accepted" : "Failed";
+	else if (!authenticated) {
+		if (authctxt->partial) {
+			authmsg = "Accepted Partial";
+		} else {
+			authmsg = "Failed";
+		}
+	} else {
+		authmsg = "Accepted";
+	}
 
 	authlog("%s %s for %s%.100s from %.200s port %d%s",
 	    authmsg,
--- auth.h.DIST	Tue Mar  5 02:53:04 2002
+++ auth.h	Thu May 23 15:05:00 2002
@@ -48,6 +48,7 @@
 struct Authctxt {
 	int		 success;
 	int		 postponed;
+	int              partial;
 	int		 valid;
 	int		 attempt;
 	int		 failures;
@@ -88,6 +89,13 @@
 	void	(*free_ctx)(void *ctx);
 };
 
+typedef struct Authorder Authorder;
+struct Authorder {
+	        char *name;
+	        Authorder *sub;
+	        Authorder *next;
+};
+
 int     auth_rhosts(struct passwd *, const char *);
 int
 auth_rhosts2(struct passwd *, const char *, const char *, const char *);
@@ -126,7 +134,7 @@
 
 Authctxt *authctxt_new(void);
 void	auth_log(Authctxt *, int, char *, char *);
-void	userauth_finish(Authctxt *, int, char *);
+void	userauth_finish(Authctxt *, int, char *, Authorder **);
 int	auth_root_allowed(char *);
 
 int	auth2_challenge(Authctxt *, char *);
--- auth2-chall.c.DIST	Tue Jan 22 13:26:40 2002
+++ auth2-chall.c	Fri May 24 10:31:19 2002
@@ -177,7 +177,6 @@
 auth2_challenge_start(Authctxt *authctxt)
 {
 	KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
-
 	debug2("auth2_challenge_start: devices %s",
 	    kbdintctxt->devices ?  kbdintctxt->devices : "<empty>");
 
@@ -245,6 +244,7 @@
 	int i, authenticated = 0, res, len;
 	u_int nresp;
 	char **response = NULL, *method;
+	Authorder *ao = NULL;
 
 	if (authctxt == NULL)
 		fatal("input_userauth_info_response: no authctxt");
@@ -307,6 +307,6 @@
 			auth2_challenge_start(authctxt);
 		}
 	}
-	userauth_finish(authctxt, authenticated, method);
+	userauth_finish(authctxt, authenticated, method, &ao);
 	xfree(method);
 }
--- auth2.c.DIST	Tue Feb 26 19:09:43 2002
+++ auth2.c	Fri May 24 11:31:05 2002
@@ -60,6 +60,9 @@
 static Authctxt	*x_authctxt = NULL;
 static int one = 1;
 
+static Authorder *authorder = NULL;
+static Authorder *lastauth = NULL;
+
 typedef struct Authmethod Authmethod;
 struct Authmethod {
 	char	*name;
@@ -77,6 +80,10 @@
 static char *authmethods_get(void);
 static int user_key_allowed(struct passwd *, Key *);
 static int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
+static Authmethod *authmethod_lookup_new(const char *name, Authorder **ao);
+static char *authmethods_get_new(void);
+
+
 
 /* auth */
 static void userauth_banner(void);
@@ -166,6 +173,7 @@
 {
 	Authctxt *authctxt = ctxt;
 	Authmethod *m = NULL;
+	Authorder *ao = NULL;
 	char *user, *service, *method, *style = NULL;
 	int authenticated = 0;
 
@@ -213,12 +221,12 @@
 	authctxt->postponed = 0;
 
 	/* try to authenticate user */
-	m = authmethod_lookup(method);
+	m = authmethod_lookup_new(method, &ao);
 	if (m != NULL) {
 		debug2("input_userauth_request: try method %s", method);
 		authenticated =	m->userauth(authctxt);
 	}
-	userauth_finish(authctxt, authenticated, method);
+	userauth_finish(authctxt, authenticated, method, &ao);
 
 	xfree(service);
 	xfree(user);
@@ -226,9 +234,12 @@
 }
 
 void
-userauth_finish(Authctxt *authctxt, int authenticated, char *method)
+userauth_finish(Authctxt *authctxt, int authenticated, char *method, Authorder **am)
 {
 	char *methods;
+	Authorder *ao;
+
+	ao=*am;
 
 	if (!authctxt->valid && authenticated)
 		fatal("INTERNAL ERROR: authenticated invalid user %s",
@@ -245,6 +256,13 @@
 		authenticated = 0;
 #endif /* USE_PAM */
 
+	if (ao !=NULL) {
+		if (authenticated && (ao->sub != NULL)) {
+	        	authctxt->partial = 1;
+             		authenticated = 0;
+         		lastauth = ao;
+		}
+	}
 	/* Log before sending the reply */
 	auth_log(authctxt, authenticated, method, " ssh2");
 
@@ -261,18 +279,23 @@
 		/* now we can break out */
 		authctxt->success = 1;
 	} else {
-		if (authctxt->failures++ > AUTH_FAIL_MAX) {
+		if (!authctxt->partial) {
 #ifdef WITH_AIXAUTHENTICATE
 			loginfailed(authctxt->user,
 			    get_canonical_hostname(options.verify_reverse_mapping),
 			    "ssh");
 #endif /* WITH_AIXAUTHENTICATE */
-			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
+			if (authctxt->failures++ > AUTH_FAIL_MAX)
+				packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
-		methods = authmethods_get();
+		methods = authmethods_get_new();
 		packet_start(SSH2_MSG_USERAUTH_FAILURE);
 		packet_put_cstring(methods);
-		packet_put_char(0);	/* XXX partial success, unused */
+		if (authctxt->partial) {
+			packet_put_char(1); /* Partial Auth */
+		} else {
+			packet_put_char(0);
+		}
 		packet_send();
 		packet_write_wait();
 		xfree(methods);
@@ -313,10 +336,29 @@
 static int
 userauth_none(Authctxt *authctxt)
 {
+	Authorder *ao, *ap;
+	int r;
+
 	/* disable method "none", only allowed one time */
-	Authmethod *m = authmethod_lookup("none");
-	if (m != NULL)
-		m->enabled = NULL;
+	ao = authorder;
+	ap = NULL;
+	while (((r = strcmp("none", ao->name)) != 0) && (ao->next != NULL)) {
+		ap = ao;
+		ao = ao->next;
+	}
+	if (r == 0) {
+		if (ap == NULL) {
+			authorder = ao->next;
+			xfree(ao->name);
+			/* If none had sub-methods, we leak memory here, but that should never happen */
+			xfree(ao);
+		} else {
+			ap->next = ao->next;
+			xfree(ao->name);
+			/* If none had sub-methods, we leak memory here, but that should never happen */
+			xfree(ao);
+		}
+	}
 	packet_check_eom();
 	userauth_banner();
 
@@ -622,6 +664,127 @@
 	return list;
 }
 
+static void 
+authorder_init(void) {
+	char *cp, sep;
+	int i, j, k, r;
+	Authorder *ao, *as;
+
+	/* none is magic */
+	authorder = (Authorder *) xmalloc(sizeof(Authorder));
+	authorder->name = (char *) xmalloc(5);
+	strlcpy(authorder->name, "none", 5);
+	authorder->sub = NULL;
+	authorder->next = NULL;         
+
+	sep = ',';
+
+	cp = options.authorder;
+	ao = authorder;
+	while (*cp != NULL) {
+		/* strip leading cruft */
+		i = strspn(cp, ",;      ");
+		cp += i;
+		if (*cp == NULL) {
+			break;
+		}
+		/* find name */
+		i = strcspn(cp, ",:     ");
+		j = strcspn(cp+i, ",:");
+		if (i > 0) {
+			switch(sep) {
+				case ':':
+					if (ao->sub == NULL) {
+						ao->sub = (Authorder *) xmalloc(sizeof(Authorder));
+						as = ao->sub;
+						as->name = NULL;
+						as->sub = NULL;
+						as->next = NULL;
+						r = 1;
+					} else {
+						as = ao->sub;
+						while ((r = strncmp(as->name, cp, i)) != 0) {
+							if (as->next != NULL) {
+								as = as->next;
+							} else {
+								break;
+							}
+						}
+					}
+					if (r != 0) {
+						if (as->name != NULL) {
+							as->next = (Authorder *) xmalloc(sizeof(Authorder));
+							as = as->next;
+						}
+						as->name = (char *) xmalloc(i+1);
+						for (k=0; k<i; k++) {
+							as->name[k] = tolower(cp[k]);
+						}
+						as->name[i] = '\0';
+						as->sub = NULL;
+						as->next = NULL;
+						ao = as;
+					}
+					ao = as;
+					break;
+				case ',':
+					ao = authorder;
+					while(((r = strncmp(ao->name, cp, i)) != 0) && (ao->next != NULL)) {
+						ao = ao->next;
+					}
+					if (r != 0) {
+						ao->next = (Authorder *) xmalloc(sizeof(Authorder));
+						ao = ao->next;
+						ao->name = (char *) xmalloc(i+1);
+						for (k=0; k<i; k++) {
+							ao->name[k] = tolower(cp[k]);
+						}
+						ao->name[i] = '\0';
+						ao->sub = NULL;
+						ao->next = NULL;
+					}
+					break;
+			}
+			sep = cp[i+j];
+		}
+		cp += i+j+1;
+	}
+}
+
+static char *
+authmethods_get_new(void)
+{
+	Authorder *ao, *as;
+	u_int size = 0;
+	char *list;
+
+	if (authorder == NULL) {
+		authorder_init();
+	}
+
+	if ((lastauth == NULL) || (lastauth->sub == NULL)) {
+		ao = authorder;
+	} else {
+		ao = lastauth->sub;
+	}
+
+	as = ao;
+	size = strlen(as->name) + 1;
+	while (as->next != NULL) {
+		as = as->next;
+		size += strlen(as->name) + 1;
+	}
+	size++;                 /* trailing '\0' */
+	list = xmalloc(size);
+	strlcpy(list, ao->name, size);
+	while(ao->next != NULL) {
+		ao = ao->next;
+		strlcat(list, DELIM, size);
+		strlcat(list, ao->name, size);
+	}
+	return list;
+}
+
 static Authmethod *
 authmethod_lookup(const char *name)
 {
@@ -635,6 +798,42 @@
 	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
 	return NULL;
 }
+
+static Authmethod *
+authmethod_lookup_new(const char *name, Authorder **am)
+{
+	Authmethod *method = NULL;
+	Authorder *ao;
+	int r;
+
+	if (name != NULL) {
+		if ((lastauth == NULL) || (lastauth->sub == NULL)) {
+			if (authorder == NULL) {
+				authorder_init();
+			}
+			ao = authorder;
+		} else {
+			ao = lastauth->sub;
+		}
+
+		while(((r = strcmp(name, ao->name)) != 0) && (ao->next != NULL)) {
+			ao = ao->next;
+		}
+
+		if (r != 0) {
+			debug2("Authentication method name \"%s\" not allowed", name);
+			return(NULL);
+		}
+
+		*am = ao;
+
+		for (method = authmethods; method->name != NULL; method++)
+			if (strcmp(name, method->name) == 0)
+				return method;
+	}
+	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
+	return NULL;
+}
 
 /* return 1 if user allows given key */
 static int
--- servconf.c.DIST	Tue Feb  5 02:26:35 2002
+++ servconf.c	Thu May 23 14:50:25 2002
@@ -106,6 +106,7 @@
 	options->max_startups = -1;
 	options->banner = NULL;
 	options->verify_reverse_mapping = -1;
+	options->authorder = NULL;
 	options->client_alive_interval = -1;
 	options->client_alive_count_max = -1;
 	options->authorized_keys_file = NULL;
@@ -222,6 +223,30 @@
 		options->max_startups_begin = options->max_startups;
 	if (options->verify_reverse_mapping == -1)
 		options->verify_reverse_mapping = 0;
+	if (options->authorder == NULL) {
+		char *s[2];
+		int size = 0, n = 0, i;
+		if (options->kbd_interactive_authentication) {
+			s[n] = "keyboard-interactive,";
+			size += strlen(s[n]);
+			n++;
+		}
+		if (options->password_authentication) {
+			s[n] = "password,";
+			size += strlen(s[n]);
+		}
+		if (options->pubkey_authentication) {
+			s[n] = "publickey,";
+			size += strlen(s[n]);
+			n++;
+		}
+		size += strlen("none")+1;
+		options->authorder = (char *) xmalloc(size);
+		for (i=0; i<n; i++) {
+			strlcat(options->authorder, s[i], size);
+		}
+		strlcpy(options->authorder, "none", size);
+	}
 	if (options->client_alive_interval == -1)
 		options->client_alive_interval = 0;
 	if (options->client_alive_count_max == -1)
@@ -264,6 +289,7 @@
 	sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
 	sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
 	sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups,
+	sAuthOrder,
 	sBanner, sVerifyReverseMapping, sHostbasedAuthentication,
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
 	sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
@@ -338,6 +364,7 @@
 	{ "banner", sBanner },
 	{ "verifyreversemapping", sVerifyReverseMapping },
 	{ "reversemappingcheck", sVerifyReverseMapping },
+	{ "authorder2", sAuthOrder },
 	{ "clientaliveinterval", sClientAliveInterval },
 	{ "clientalivecountmax", sClientAliveCountMax },
 	{ "authorizedkeysfile", sAuthorizedKeysFile },
@@ -836,6 +863,39 @@
 	case sBanner:
 		charptr = &options->banner;
 		goto parse_filename;
+	case sAuthOrder:
+		arg = strdelim(&cp);
+		if ((strstr(arg,":none") != NULL) || (strstr(arg,"none:") != NULL)){
+			fprintf(stderr, "%s line %d: none may not be or have a subauth method\n",
+					filename, linenum);
+			exit(1);
+		}
+		{
+			int i, match;
+			char *mycp;
+			mycp = arg;
+			while(((i = strcspn(mycp,",:")) != 0) && (*mycp != NULL)) {
+				match = 0;
+				if (strncasecmp(mycp,"none",i) == 0) {
+					match = 1;
+				} else if (strncasecmp(mycp,"publickey",i) == 0) {
+					match = 1;
+				} else if (strncasecmp(mycp,"password",i) == 0) {
+					match = 1;
+				} else if (strncasecmp(mycp,"keyboard-interactive",i) == 0) {
+					match = 1;
+				}
+				if (match == 0) {
+					mycp[i]='\0';
+					fprintf(stderr, "%s line %d: illegal auth method %s",
+							filename, linenum, mycp);
+					exit(1);
+				}
+				mycp += i+1;
+			}
+		}
+		options->authorder = xstrdup(arg);
+		break;
 	/*
 	 * These options can contain %X options expanded at
 	 * connect time, so that you can specify paths like:
--- servconf.h.DIST	Tue Mar  5 02:53:05 2002
+++ servconf.h	Thu May 23 14:50:25 2002
@@ -117,6 +117,7 @@
 	int	max_startups;
 	char   *banner;			/* SSH-2 banner message */
 	int	verify_reverse_mapping;	/* cross-check ip and dns */
+	char   *authorder;              /* Authentication Orders */
 	int	client_alive_interval;	/*
 					 * poke the client this often to
 					 * see if it's still there
