[RFC][PATCH] Require S/KEY before other authentication methods.

David Woodhouse dwmw2 at infradead.org
Sun Mar 2 20:43:55 EST 2003


I need a way to make sshd require S/KEY authentication to succeed before
allowing either password or public-key authentication.

Currently, we can only have S/KEY+password, by using PAM for
authentication, and configuring PAM accordingly. But PAM of course can't
handle SSH public keys.

I thought for a while that ideally we could actually use PAM to tell
sshd what methods of authentication to accept at each stage...

	require pam_ssh_skey.so
	sufficient pam_ssh_publickey.so
	sufficient pam_ssh_password.so

...etc. But PAM doesn't actually let us work like that, so it'd end up
being something more like...

	require pam_ssh.so methods=skey
	require pam_ssh.so methods=publickey,password

...and I suspect it's overkill anyway. I can't really see many other
possible combinations that people are likely to want.

Instead of going further down this path, I implemented a simple
special-case 'ChallengeResponseAuthenticationFirst' option for sshd,
which makes it do what I require, offering only skey to a client at
first, then offering other auth methods after skey has succeeded. 

Is there any point in trying to make it more generic? What other setups
are people likely to want? 

Index: auth2-kbdint.c
===================================================================
RCS file: /cvs/openssh/auth2-kbdint.c,v
retrieving revision 1.1
diff -u -p -r1.1 auth2-kbdint.c
--- auth2-kbdint.c	6 Jun 2002 20:27:56 -0000	1.1
+++ auth2-kbdint.c	1 Mar 2003 17:37:41 -0000
@@ -50,7 +50,13 @@ userauth_kbdint(Authctxt *authctxt)
 		authenticated = auth2_challenge(authctxt, devs);
 
 #ifdef USE_PAM
-	if (authenticated == 0 && options.pam_authentication_via_kbd_int)
+	/* In the normal case, try PAM if challenge-response failed.
+	   However, if this was a prerequisite challenge-response
+	   authentication attempt, and PAM auth is permitted as a
+	   secondary method, then force the client to come back
+	   with a second attempt instead. */
+	if (!options.challenge_response_authentication_first &&
+	    authenticated == 0 && options.pam_authentication_via_kbd_int)
 		authenticated = auth2_pam(authctxt);
 #endif
 	xfree(devs);
Index: auth2.c
===================================================================
RCS file: /cvs/openssh/auth2.c,v
retrieving revision 1.112
diff -u -p -r1.112 auth2.c
--- auth2.c	24 Feb 2003 00:59:27 -0000	1.112
+++ auth2.c	1 Mar 2003 17:37:41 -0000
@@ -228,16 +228,7 @@ userauth_finish(Authctxt *authctxt, int 
 	if (authctxt->postponed)
 		return;
 
-	/* XXX todo: check if multiple auth methods are needed */
-	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 {
+	if (!authenticated) {
 		if (authctxt->failures++ > AUTH_FAIL_MAX) {
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
@@ -252,6 +243,32 @@ userauth_finish(Authctxt *authctxt, int 
 		packet_send();
 		packet_write_wait();
 		xfree(methods);
+	} else if (!options.challenge_response_authentication_first) {
+		/* Success. 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 {
+		/* Our prereqisite challenge-response authentication has
+		   succeeded. Allow other methods from now on... */
+		debug("prerequisite keyboard-interactive auth succeeded");
+
+		/* And disallow challenge-response authentication so
+		   we don't just accept it twice :) */
+		options.challenge_response_authentication_first = 0;
+		options.challenge_response_authentication = 0;
+		options.kbd_interactive_authentication = options.pam_authentication_via_kbd_int;
+
+		methods = authmethods_get();
+		packet_start(SSH2_MSG_USERAUTH_FAILURE);
+		packet_put_cstring(methods);
+		packet_put_char(1);	/* XXX partial success, used */
+		packet_send();
+		packet_write_wait();
+		xfree(methods);
 	}
 }
 
@@ -272,6 +289,11 @@ authmethods_get(void)
 	char *list;
 	int i;
 
+	/* If challenge-response is a prerequiste, advertise
+	   that only */
+	if (options.challenge_response_authentication_first)
+		return xstrdup("keyboard-interactive");
+
 	buffer_init(&b);
 	for (i = 0; authmethods[i] != NULL; i++) {
 		if (strcmp(authmethods[i]->name, "none") == 0)
@@ -294,6 +316,10 @@ static Authmethod *
 authmethod_lookup(const char *name)
 {
 	int i;
+
+	if (options.challenge_response_authentication_first &&
+	    strcmp(name, "keyboard-interactive") && strcmp(name, "none"))
+	    return NULL;
 
 	if (name != NULL)
 		for (i = 0; authmethods[i] != NULL; i++)
Index: monitor.c
===================================================================
RCS file: /cvs/openssh/monitor.c,v
retrieving revision 1.35
diff -u -p -r1.35 monitor.c
--- monitor.c	24 Feb 2003 01:03:39 -0000	1.35
+++ monitor.c	1 Mar 2003 17:37:41 -0000
@@ -297,6 +297,15 @@ monitor_child_preauth(struct monitor *pm
 			if (!authenticated)
 				authctxt->failures++;
 		}
+		if (options.challenge_response_authentication_first &&
+		    authenticated &&
+		    (!strcmp(auth_method, "skey") || !strcmp(auth_method, "bsdauth"))) {
+			debug2("prerequisite keyboard-interactive authentication succeeded");
+			options.challenge_response_authentication_first = 0;
+			options.kbd_interactive_authentication = options.pam_authentication_via_kbd_int;
+			options.challenge_response_authentication = 0;
+			authenticated = 0;
+		}
 	}
 
 	if (!authctxt->valid)
Index: servconf.c
===================================================================
RCS file: /cvs/openssh/servconf.c,v
retrieving revision 1.98
diff -u -p -r1.98 servconf.c
--- servconf.c	24 Feb 2003 01:04:34 -0000	1.98
+++ servconf.c	1 Mar 2003 17:37:42 -0000
@@ -100,6 +100,7 @@ initialize_server_options(ServerOptions 
 	options->password_authentication = -1;
 	options->kbd_interactive_authentication = -1;
 	options->challenge_response_authentication = -1;
+	options->challenge_response_authentication_first = -1;
 	options->permit_empty_passwd = -1;
 	options->permit_user_env = -1;
 	options->use_login = -1;
@@ -222,6 +223,13 @@ fill_default_server_options(ServerOption
 		options->kbd_interactive_authentication = 0;
 	if (options->challenge_response_authentication == -1)
 		options->challenge_response_authentication = 1;
+	if (options->challenge_response_authentication_first == -1)
+		options->challenge_response_authentication_first = 0;
+	if (options->challenge_response_authentication_first &&
+	    !options->challenge_response_authentication) {
+		error("ChallengeResponse authentication cannot be first if it is disabled");
+		options->challenge_response_authentication_first = 0;
+	}		
 	if (options->permit_empty_passwd == -1)
 		options->permit_empty_passwd = 0;
 	if (options->permit_user_env == -1)
@@ -289,7 +297,7 @@ typedef enum {
 #ifdef AFS
 	sAFSTokenPassing,
 #endif
-	sChallengeResponseAuthentication,
+	sChallengeResponseAuthentication, sChallengeResponseAuthenticationFirst,
 	sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress,
 	sPrintMotd, sPrintLastLog, sIgnoreRhosts,
 	sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
@@ -345,6 +353,7 @@ static struct {
 	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
 	{ "challengeresponseauthentication", sChallengeResponseAuthentication },
 	{ "skeyauthentication", sChallengeResponseAuthentication }, /* alias */
+	{ "challengeresponseauthenticationfirst", sChallengeResponseAuthenticationFirst },
 	{ "checkmail", sDeprecated },
 	{ "listenaddress", sListenAddress },
 	{ "printmotd", sPrintMotd },
@@ -679,6 +688,10 @@ parse_flag:
 
 	case sChallengeResponseAuthentication:
 		intptr = &options->challenge_response_authentication;
+		goto parse_flag;
+
+	case sChallengeResponseAuthenticationFirst:
+		intptr = &options->challenge_response_authentication_first;
 		goto parse_flag;
 
 	case sPrintMotd:
Index: servconf.h
===================================================================
RCS file: /cvs/openssh/servconf.h,v
retrieving revision 1.50
diff -u -p -r1.50 servconf.h
--- servconf.h	1 Aug 2002 01:28:39 -0000	1.50
+++ servconf.h	1 Mar 2003 17:37:42 -0000
@@ -95,6 +95,9 @@ typedef struct {
 						 * authentication. */
 	int     kbd_interactive_authentication;	/* If true, permit */
 	int     challenge_response_authentication;
+	int     challenge_response_authentication_first; /* If true, force
+					    clients to use challenge-response
+					    first, before other methods */
 	int     permit_empty_passwd;	/* If false, do not permit empty
 					 * passwords. */
 	int     permit_user_env;	/* If true, read ~/.ssh/environment */




-- 
dwmw2




More information about the openssh-unix-dev mailing list