Feature request: FAIL_DELAY-support for sshd

Bjoern Voigt bjoern at cs.tu-berlin.de
Sat Feb 5 00:58:52 EST 2005


Darren Tucker <dtucker at zip.com.au>:

> (BTW, pam_fail_delay is AFAIK LinuxPAM only.)

Yes, but it's possible to test the existance of pam_fail_delay() in PAM
with "#ifdef HAVE_PAM_FAIL_DELAY" (source:
/usr/include/security/_pam_types.h in Linux-PAM).

> I checked the change log, the changes I was referring to didn't go in until
> 20050118, and there seems to be some problem with snaps being propogated to
> the ftp sites.
> 
> In the mean time I've temporarily put up an unofficial snap at
> http://www.zip.com.au/~dtucker/tmp/openssh-20050203.tar.gz

Thank you for this link. I have tested it.

These are my test results:

1) With this current openssh snapshot I couldn't see delays at
   first. The reason may be, that my PAM-installation (SuSE Linux 9.2)
   has no default delays.

2) I inserted pam_fail_delay() before pam_authenticate() in auth-pam.c
   (see my attached patch openssh-fail-delay.diff). After that I can see
   the same delays for wrong passwords and wrong usernames. THANK
   YOU. This is, what I wanted! :-)

   One small problem remains: I get the delays only with
   ChallengeResponseAuthentication, not with PasswordAuthentication. I
   wonder a bit about this.

I like to test this new feature on my Internet server (SuSE Linux 9.2
with a SuSE-RPM of OpenSSH-3.9p1). So I looked in ChangeLog of your
snapshot:

20050120:
  ...
  - (dtucker) [auth-pam.c] Bug #971: Prevent leaking information about
    user existence via keyboard-interactive/pam, in conjunction with
    previous auth2-chall.c change; with Colin Watson and djm.

As you mentioned "auth-pam.c" I patched auth-pam.c (from current) to my
openssh-3.9p1 (see my attached patch
openssh-3.9p1-to-20050203-user-exists.diff). Unfortunately now I have
the different delays as a result of a wrong user or a wrong password
again.

Do you have the whole patch for your ChangeLog-entry? I already looked
in www.openssh.org's CVS archive, but there is only OpenBSD's ssh source
in CVS.

Probably I also need some changes in other files, not only in
pam-auth.c?

Regards, Björn
-------------- next part --------------
--- openssh-3.9p1/auth-pam.c	2005-02-04 13:42:57.475718288 +0100
+++ openssh-20050203/auth-pam.c	2005-02-04 13:29:49.403523472 +0100
@@ -47,7 +47,7 @@
 
 /* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
 #include "includes.h"
-RCSID("$Id: auth-pam.c,v 1.114 2004/08/16 13:12:06 dtucker Exp $");
+RCSID("$Id: auth-pam.c,v 1.121 2005/01/20 02:29:51 dtucker Exp $");
 
 #ifdef USE_PAM
 #if defined(HAVE_SECURITY_PAM_APPL_H)
@@ -186,6 +186,7 @@
 static char **sshpam_env = NULL;
 static Authctxt *sshpam_authctxt = NULL;
 static const char *sshpam_password = NULL;
+static char badpw[] = "\b\n\r\177INCORRECT";
 
 /* Some PAM implementations don't implement this */
 #ifndef HAVE_PAM_GETENVLIST
@@ -389,8 +390,7 @@
 	u_int i;
 	const char *pam_user;
 
-	const char **ptr_pam_user = &pam_user;
-	pam_get_item(sshpam_handle, PAM_USER, (const void **)ptr_pam_user);
+	pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user);
 	environ[0] = NULL;
 
 	if (sshpam_authctxt != NULL) {
@@ -491,6 +492,51 @@
 
 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
 
+static int
+sshpam_store_conv(int n, struct pam_message **msg,
+    struct pam_response **resp, void *data)
+{
+	struct pam_response *reply;
+	int i;
+	size_t len;
+
+	debug3("PAM: %s called with %d messages", __func__, n);
+	*resp = NULL;
+
+	if (n <= 0 || n > PAM_MAX_NUM_MSG)
+		return (PAM_CONV_ERR);
+
+	if ((reply = malloc(n * sizeof(*reply))) == NULL)
+		return (PAM_CONV_ERR);
+	memset(reply, 0, n * sizeof(*reply));
+
+	for (i = 0; i < n; ++i) {
+		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+		case PAM_ERROR_MSG:
+		case PAM_TEXT_INFO:
+			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
+			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
+			buffer_append(&loginmsg, "\n", 1 );
+			reply[i].resp_retcode = PAM_SUCCESS;
+			break;
+		default:
+			goto fail;
+		}
+	}
+	*resp = reply;
+	return (PAM_SUCCESS);
+
+ fail:
+	for(i = 0; i < n; i++) {
+		if (reply[i].resp != NULL)
+			xfree(reply[i].resp);
+	}
+	xfree(reply);
+	return (PAM_CONV_ERR);
+}
+
+static struct pam_conv store_conv = { sshpam_store_conv, NULL };
+
 void
 sshpam_cleanup(void)
 {
@@ -516,12 +562,11 @@
 {
 	extern char *__progname;
 	const char *pam_rhost, *pam_user, *user = authctxt->user;
-	const char **ptr_pam_user = &pam_user;
 
 	if (sshpam_handle != NULL) {
 		/* We already have a PAM context; check if the user matches */
 		sshpam_err = pam_get_item(sshpam_handle,
-			PAM_USER, (const void **)ptr_pam_user);
+		    PAM_USER, (void **)&pam_user);
 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
 			return (0);
 		pam_end(sshpam_handle, sshpam_err);
@@ -529,7 +574,7 @@
 	}
 	debug("PAM: initializing for \"%s\"", user);
 	sshpam_err =
-	    pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
+	    pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
 	sshpam_authctxt = authctxt;
 
 	if (sshpam_err != PAM_SUCCESS) {
@@ -611,7 +656,7 @@
 	size_t plen;
 	u_char type;
 	char *msg;
-	size_t len;
+	size_t len, mlen;
 
 	debug3("PAM: %s entering", __func__);
 	buffer_init(&buffer);
@@ -624,22 +669,27 @@
 	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
 		type = buffer_get_char(&buffer);
 		msg = buffer_get_string(&buffer, NULL);
+		mlen = strlen(msg);
 		switch (type) {
 		case PAM_PROMPT_ECHO_ON:
 		case PAM_PROMPT_ECHO_OFF:
 			*num = 1;
-			len = plen + strlen(msg) + 1;
+			len = plen + mlen + 1;
 			**prompts = xrealloc(**prompts, len);
-			plen += snprintf(**prompts + plen, len, "%s", msg);
+			strlcpy(**prompts + plen, msg, len - plen);
+			plen += mlen;
 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
 			xfree(msg);
 			return (0);
 		case PAM_ERROR_MSG:
 		case PAM_TEXT_INFO:
 			/* accumulate messages */
-			len = plen + strlen(msg) + 2;
+			len = plen + mlen + 2;
 			**prompts = xrealloc(**prompts, len);
-			plen += snprintf(**prompts + plen, len, "%s\n", msg);
+			strlcpy(**prompts + plen, msg, len - plen);
+			plen += mlen;
+			strlcat(**prompts + plen, "\n", len - plen);
+			plen++;
 			xfree(msg);
 			break;
 		case PAM_SUCCESS:
@@ -653,9 +703,13 @@
 				**prompts = NULL;
 			}
 			if (type == PAM_SUCCESS) {
-#ifndef USE_POSIX_THREADS
+				if (!sshpam_authctxt->valid ||
+				    (sshpam_authctxt->pw->pw_uid == 0 &&
+				    options.permit_root_login != PERMIT_YES))
+					fatal("Internal error: PAM auth "
+					    "succeeded when it should have "
+					    "failed");
 				import_environments(&buffer);
-#endif
 				*num = 0;
 				**echo_on = 0;
 				ctxt->pam_done = 1;
@@ -700,7 +754,12 @@
 		return (-1);
 	}
 	buffer_init(&buffer);
-	buffer_put_cstring(&buffer, *resp);
+	if (sshpam_authctxt->valid &&
+	    (sshpam_authctxt->pw->pw_uid != 0 ||
+	     options.permit_root_login == PERMIT_YES))
+		buffer_put_cstring(&buffer, *resp);
+	else
+		buffer_put_cstring(&buffer, badpw);
 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
 		buffer_free(&buffer);
 		return (-1);
@@ -763,11 +822,13 @@
 u_int
 do_pam_account(void)
 {
+	debug("%s: called", __func__);
 	if (sshpam_account_status != -1)
 		return (sshpam_account_status);
 
 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
-	debug3("PAM: %s pam_acct_mgmt = %d", __func__, sshpam_err);
+	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
+	    pam_strerror(sshpam_handle, sshpam_err));
 	
 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
 		sshpam_account_status = 0;
@@ -797,7 +858,7 @@
 do_pam_setcred(int init)
 {
 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
-	    (const void *)&null_conv);
+	    (const void *)&store_conv);
 	if (sshpam_err != PAM_SUCCESS)
 		fatal("PAM: failed to set PAM_CONV: %s",
 		    pam_strerror(sshpam_handle, sshpam_err));
@@ -898,51 +959,6 @@
 		    pam_strerror(sshpam_handle, sshpam_err));
 }
 
-static int
-sshpam_store_conv(int n, struct pam_message **msg,
-    struct pam_response **resp, void *data)
-{
-	struct pam_response *reply;
-	int i;
-	size_t len;
-
-	debug3("PAM: %s called with %d messages", __func__, n);
-	*resp = NULL;
-
-	if (n <= 0 || n > PAM_MAX_NUM_MSG)
-		return (PAM_CONV_ERR);
-
-	if ((reply = malloc(n * sizeof(*reply))) == NULL)
-		return (PAM_CONV_ERR);
-	memset(reply, 0, n * sizeof(*reply));
-
-	for (i = 0; i < n; ++i) {
-		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
-		case PAM_ERROR_MSG:
-		case PAM_TEXT_INFO:
-			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
-			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
-			buffer_append(&loginmsg, "\n", 1 );
-			reply[i].resp_retcode = PAM_SUCCESS;
-			break;
-		default:
-			goto fail;
-		}
-	}
-	*resp = reply;
-	return (PAM_SUCCESS);
-
- fail:
-	for(i = 0; i < n; i++) {
-		if (reply[i].resp != NULL)
-			xfree(reply[i].resp);
-	}
-	xfree(reply);
-	return (PAM_CONV_ERR);
-}
-
-static struct pam_conv store_conv = { sshpam_store_conv, NULL };
-
 void
 do_pam_session(void)
 {
@@ -953,10 +969,21 @@
 		fatal("PAM: failed to set PAM_CONV: %s",
 		    pam_strerror(sshpam_handle, sshpam_err));
 	sshpam_err = pam_open_session(sshpam_handle, 0);
-	if (sshpam_err != PAM_SUCCESS)
-		fatal("PAM: pam_open_session(): %s",
+	if (sshpam_err == PAM_SUCCESS)
+		sshpam_session_open = 1;
+	else {
+		sshpam_session_open = 0;
+		disable_forwarding();
+		error("PAM: pam_open_session(): %s",
 		    pam_strerror(sshpam_handle, sshpam_err));
-	sshpam_session_open = 1;
+	}
+
+}
+
+int
+is_pam_session_open(void)
+{
+	return sshpam_session_open;
 }
 
 /*
@@ -1079,7 +1106,6 @@
 {
 	int flags = (options.permit_empty_passwd == 0 ?
 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
-	static char badpw[] = "\b\n\r\177INCORRECT";
 
 	if (!options.use_pam || sshpam_handle == NULL)
 		fatal("PAM: %s called when PAM disabled or failed to "
-------------- next part --------------
--- auth-pam.c.orig	2005-02-04 13:44:29.553720304 +0100
+++ auth-pam.c	2005-02-04 13:50:41.013249872 +0100
@@ -411,6 +411,12 @@
 	    (const void *)&sshpam_conv);
 	if (sshpam_err != PAM_SUCCESS)
 		goto auth_fail;
+#ifdef HAVE_PAM_FAIL_DELAY
+	/* wait around 10 seconds, if PAM-authentication fails */
+	sshpam_err = pam_fail_delay(sshpam_handle, 10000000);
+	if (sshpam_err != PAM_SUCCESS)
+		goto auth_fail;	
+#endif
 	sshpam_err = pam_authenticate(sshpam_handle, flags);
 	if (sshpam_err != PAM_SUCCESS)
 		goto auth_fail;


More information about the openssh-unix-dev mailing list