- markus@cvs.openbsd.org 2013/03/07 19:27:25
     [auth.h auth2-chall.c auth2.c monitor.c sshd_config.5]
     add submethod support to AuthenticationMethods; ok and freedback djm@
diff --git a/ChangeLog b/ChangeLog
index 38f6fa8..7be6f7b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -19,6 +19,9 @@
      have included a style (e.g. "root:skey") when checking public key
      signatures. Fixes public key and hostbased auth when the client specified
      a style; ok markus@
+   - markus@cvs.openbsd.org 2013/03/07 19:27:25
+     [auth.h auth2-chall.c auth2.c monitor.c sshd_config.5]
+     add submethod support to AuthenticationMethods; ok and freedback djm@
 
 20130418
  - (djm) [config.guess config.sub] Update to last versions before they switch
diff --git a/auth.h b/auth.h
index c6fe847..7ff59f1 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.72 2012/12/02 20:34:09 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.73 2013/03/07 19:27:25 markus Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -157,8 +157,9 @@
 
 char	*auth2_read_banner(void);
 int	 auth2_methods_valid(const char *, int);
-int	 auth2_update_methods_lists(Authctxt *, const char *);
+int	 auth2_update_methods_lists(Authctxt *, const char *, const char *);
 int	 auth2_setup_methods_lists(Authctxt *);
+int	 auth2_method_allowed(Authctxt *, const char *, const char *);
 
 void	privsep_challenge_enable(void);
 
diff --git a/auth2-chall.c b/auth2-chall.c
index 6505d40..ed1acdd 100644
--- a/auth2-chall.c
+++ b/auth2-chall.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-chall.c,v 1.36 2012/12/03 00:14:06 djm Exp $ */
+/* $OpenBSD: auth2-chall.c,v 1.37 2013/03/07 19:27:25 markus Exp $ */
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 2001 Per Allansson.  All rights reserved.
@@ -155,7 +155,7 @@
 }
 /* get next device */
 static int
-kbdint_next_device(KbdintAuthctxt *kbdintctxt)
+kbdint_next_device(Authctxt *authctxt, KbdintAuthctxt *kbdintctxt)
 {
 	size_t len;
 	char *t;
@@ -169,9 +169,13 @@
 
 		if (len == 0)
 			break;
-		for (i = 0; devices[i]; i++)
+		for (i = 0; devices[i]; i++) {
+			if (!auth2_method_allowed(authctxt,
+			    "keyboard-interactive", devices[i]->name))
+				continue;
 			if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
 				kbdintctxt->device = devices[i];
+		}
 		t = kbdintctxt->devices;
 		kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
 		xfree(t);
@@ -221,7 +225,7 @@
 	debug2("auth2_challenge_start: devices %s",
 	    kbdintctxt->devices ?  kbdintctxt->devices : "<empty>");
 
-	if (kbdint_next_device(kbdintctxt) == 0) {
+	if (kbdint_next_device(authctxt, kbdintctxt) == 0) {
 		auth2_challenge_stop(authctxt);
 		return 0;
 	}
diff --git a/auth2.c b/auth2.c
index e367a10..e444821 100644
--- a/auth2.c
+++ b/auth2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2.c,v 1.126 2012/12/02 20:34:09 djm Exp $ */
+/* $OpenBSD: auth2.c,v 1.127 2013/03/07 19:27:25 markus Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -98,8 +98,12 @@
 /* helper */
 static Authmethod *authmethod_lookup(Authctxt *, const char *);
 static char *authmethods_get(Authctxt *authctxt);
-static int method_allowed(Authctxt *, const char *);
-static int list_starts_with(const char *, const char *);
+
+#define MATCH_NONE	0	/* method or submethod mismatch */
+#define MATCH_METHOD	1	/* method matches (no submethod specified) */
+#define MATCH_BOTH	2	/* method and submethod match */
+#define MATCH_PARTIAL	3	/* method matches, submethod can't be checked */
+static int list_starts_with(const char *, const char *, const char *);
 
 char *
 auth2_read_banner(void)
@@ -316,7 +320,7 @@
 	}
 
 	if (authenticated && options.num_auth_methods != 0) {
-		if (!auth2_update_methods_lists(authctxt, method)) {
+		if (!auth2_update_methods_lists(authctxt, method, submethod)) {
 			authenticated = 0;
 			partial = 1;
 		}
@@ -387,8 +391,9 @@
  * methods list. Returns 1 if allowed, or no methods lists configured.
  * 0 otherwise.
  */
-static int
-method_allowed(Authctxt *authctxt, const char *method)
+int
+auth2_method_allowed(Authctxt *authctxt, const char *method,
+    const char *submethod)
 {
 	u_int i;
 
@@ -399,7 +404,8 @@
 	if (options.num_auth_methods == 0)
 		return 1;
 	for (i = 0; i < authctxt->num_auth_methods; i++) {
-		if (list_starts_with(authctxt->auth_methods[i], method))
+		if (list_starts_with(authctxt->auth_methods[i], method,
+		    submethod) != MATCH_NONE)
 			return 1;
 	}
 	return 0;
@@ -419,7 +425,8 @@
 		if (authmethods[i]->enabled == NULL ||
 		    *(authmethods[i]->enabled) == 0)
 			continue;
-		if (!method_allowed(authctxt, authmethods[i]->name))
+		if (!auth2_method_allowed(authctxt, authmethods[i]->name,
+		    NULL))
 			continue;
 		if (buffer_len(&b) > 0)
 			buffer_append(&b, ",", 1);
@@ -442,7 +449,8 @@
 			if (authmethods[i]->enabled != NULL &&
 			    *(authmethods[i]->enabled) != 0 &&
 			    strcmp(name, authmethods[i]->name) == 0 &&
-			    method_allowed(authctxt, authmethods[i]->name))
+			    auth2_method_allowed(authctxt,
+			    authmethods[i]->name, NULL))
 				return authmethods[i];
 	debug2("Unrecognized authentication method name: %s",
 	    name ? name : "NULL");
@@ -457,7 +465,7 @@
 int
 auth2_methods_valid(const char *_methods, int need_enable)
 {
-	char *methods, *omethods, *method;
+	char *methods, *omethods, *method, *p;
 	u_int i, found;
 	int ret = -1;
 
@@ -468,6 +476,8 @@
 	omethods = methods = xstrdup(_methods);
 	while ((method = strsep(&methods, ",")) != NULL) {
 		for (found = i = 0; !found && authmethods[i] != NULL; i++) {
+			if ((p = strchr(method, ':')) != NULL)
+				*p = '\0';
 			if (strcmp(method, authmethods[i]->name) != 0)
 				continue;
 			if (need_enable) {
@@ -533,15 +543,30 @@
 }
 
 static int
-list_starts_with(const char *methods, const char *method)
+list_starts_with(const char *methods, const char *method,
+    const char *submethod)
 {
 	size_t l = strlen(method);
+	int match;
+	const char *p;
 
 	if (strncmp(methods, method, l) != 0)
-		return 0;
-	if (methods[l] != ',' && methods[l] != '\0')
-		return 0;
-	return 1;
+		return MATCH_NONE;
+	p = methods + l;
+	match = MATCH_METHOD;
+	if (*p == ':') {
+		if (!submethod)
+			return MATCH_PARTIAL;
+		l = strlen(submethod);
+		p += 1;
+		if (strncmp(submethod, p, l))
+			return MATCH_NONE;
+		p += l;
+		match = MATCH_BOTH;
+	}
+	if (*p != ',' && *p != '\0')
+		return MATCH_NONE;
+	return match;
 }
 
 /*
@@ -550,14 +575,21 @@
  * if it did.
  */
 static int
-remove_method(char **methods, const char *method)
+remove_method(char **methods, const char *method, const char *submethod)
 {
-	char *omethods = *methods;
+	char *omethods = *methods, *p;
 	size_t l = strlen(method);
+	int match;
 
-	if (!list_starts_with(omethods, method))
+	match = list_starts_with(omethods, method, submethod);
+	if (match != MATCH_METHOD && match != MATCH_BOTH)
 		return 0;
-	*methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
+	p = omethods + l;
+	if (submethod && match == MATCH_BOTH)
+		p += 1 + strlen(submethod); /* include colon */
+	if (*p == ',')
+		p++;
+	*methods = xstrdup(p);
 	free(omethods);
 	return 1;
 }
@@ -569,13 +601,15 @@
  * Returns 1 if the method completed any authentication list or 0 otherwise.
  */
 int
-auth2_update_methods_lists(Authctxt *authctxt, const char *method)
+auth2_update_methods_lists(Authctxt *authctxt, const char *method,
+    const char *submethod)
 {
 	u_int i, found = 0;
 
 	debug3("%s: updating methods list after \"%s\"", __func__, method);
 	for (i = 0; i < authctxt->num_auth_methods; i++) {
-		if (!remove_method(&(authctxt->auth_methods[i]), method))
+		if (!remove_method(&(authctxt->auth_methods[i]), method,
+		    submethod))
 			continue;
 		found = 1;
 		if (*authctxt->auth_methods[i] == '\0') {
diff --git a/monitor.c b/monitor.c
index 34d7e18..c0471a1 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.121 2013/03/07 00:19:59 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.122 2013/03/07 19:27:25 markus Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -392,7 +392,7 @@
 				    "with SSH protocol 1");
 			if (authenticated &&
 			    !auth2_update_methods_lists(authctxt,
-			    auth_method)) {
+			    auth_method, auth_submethod)) {
 				debug3("%s: method %s: partial", __func__,
 				    auth_method);
 				authenticated = 0;
@@ -949,9 +949,10 @@
 	debug3("%s: sending authenticated: %d", __func__, authok);
 	mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m);
 
-	if (compat20)
-		auth_method = "keyboard-interactive"; /* XXX auth_submethod */
-	else
+	if (compat20) {
+		auth_method = "keyboard-interactive";
+		auth_submethod = "bsdauth";
+	} else
 		auth_method = "bsdauth";
 
 	return (authok != 0);
diff --git a/sshd_config.5 b/sshd_config.5
index cfa4806..4fe3c55 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -33,8 +33,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd_config.5,v 1.156 2013/02/06 00:20:42 dtucker Exp $
-.Dd $Mdocdate: February 6 2013 $
+.\" $OpenBSD: sshd_config.5,v 1.157 2013/03/07 19:27:25 markus Exp $
+.Dd $Mdocdate: March 7 2013 $
 .Dt SSHD_CONFIG 5
 .Os
 .Sh NAME
@@ -180,6 +180,20 @@
 so for this example, it would not be possible to attempt password or
 keyboard-interactive authentication before public key.
 .Pp
+For keyboard interactive authentication it is also possible to
+restrict authentication to a specific device by appending a
+colon followed by the device identifier
+.Dq bsdauth ,
+.Dq pam ,
+or
+.Dq skey ,
+depending on the server configuration.
+For example,
+.Dq keyboard-interactive:bsdauth
+would restrict keyboard interactive authentication to the
+.Dq bsdauth
+device.
+.Pp
 This option is only available for SSH protocol 2 and will yield a fatal
 error if enabled if protocol 1 is also enabled.
 Note that each authentication method listed should also be explicitly enabled