Requiring multiple auth mechanisms

Scott Omar Burch scott.burch at camberwind.com
Thu Apr 8 03:53:53 EST 2004


Jeff,

You might also want to look at the code for auth selection that was 
written here:

http://sweb.cz/v_t_m/ (This individual also has written patches for 
securid that work very well).

-Scott

Jefferson Ogata wrote:
> 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.
> 
> 
> ------------------------------------------------------------------------
> 
> --- 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 at mindrot.org
> http://www.mindrot.org/mailman/listinfo/openssh-unix-dev




More information about the openssh-unix-dev mailing list