[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