[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openssh-unix-dev
Subject:    Requiring multiple auth mechanisms
From:       Jefferson Ogata <Jefferson.Ogata () noaa ! gov>
Date:       2004-04-07 14:39:19
Message-ID: 40741297.2090804 () noaa ! gov
[Download RAW message or body]

I looked around for a while, but couldn't find any code for requiring multiple 
authentication mechanisms in openssh. So I wrote an implemention.

I thought at first I should change the PasswordAuthentication, 
PubkeyAuthentication, etc. keywords to allow no/yes/required. But there's some 
funky stuff in auth2.c with respect to keyboard interactive auth that would make 
this kind of gnarly, semantics-wise.

I also thought about providing a new keyword to specify a list of required 
authentication mechanisms. But then you have to make sure those mechanisms are 
also enabled, and it's easy to write conflicting configurations. In addition, if 
a list of required auth mechs is given, then enabling mechanisms that are not 
required is pointless, because they won't be sufficient.

So my final decision, for the sake of simplicity, was to add a 
"NumRequiredAuthMethods" keyword, which defaults to 1. If you set it to 2, the 
client must pass at least two of the enabled auth methods. I'm using the term 
"methods" here because I'm only counting general auth methods as defined in 
auth2.c's "authmethods" array, namely publickey, password, keyboard-interactive, 
and hostbased. So there may be multiple types of keyboard-interactive auth, but 
keyboard-interactive only counts as a single method.

So, for example, if you have PasswordAuthentication and PubkeyAuthentication 
enabled, and set NumRequiredAuthMethods to 2, you will have to pass both types. 
But PAMAuthenticationViaKbdInt and ChallengeResponseAuthentication are the same 
authentication method (keyboard-interactive), so if you want to require 2 
classes, you'll have to have at least one of the other methods enabled as well.

I don't know much about some of the supported authentication types, particularly 
pam, so I'm not totally sure my approach makes sense for everyone's needs. My 
particular need was to require both public key and S/KEY factors so that 
one-time passwords can be combined with a strong electronic authenticator. I 
don't trust my users not to end up trojaned with a keylogger, so I need OTP, but 
I also want a public key in case someone loses his S/KEY cheat sheet.

The attached patch is designed for Red Hat's openssh-3.1p1-14 SRPM (add as 
Patch14, use -p1 on patch line in %prep). It should work against openssh-3.8 
with slight tweaks (authmethods changed in auth2.c). If people need a patch 
against 3.8, I can build it; just ask.

I would really appreciate it if anyone with interest could vet this for stupid 
mistakes.

-- 
Jefferson Ogata <Jefferson.Ogata@noaa.gov>
NOAA Computer Incident Response Team (N-CIRT) <ncirt@noaa.gov>

["openssh-3.1p1-multipleauth.patch" (text/plain)]

--- 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


_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
http://www.mindrot.org/mailman/listinfo/openssh-unix-dev


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic