[PATCH] PAM/abandon userauth (was: keyboard-interactive)

Frank Cusack fcusack at fcusack.com
Fri Jan 11 19:11:25 EST 2002


On Thu, Jan 10, 2002 at 09:48:57AM +0100, Markus Friedl wrote:
> yes, the only thing that needs to be handled is:
> 
> 	(1) C->S:	USERAUTH_REQUEST(kbdint)
> 	(2) S->C:	INFO_REQUEST
> 	(3) C->S:	USERAUTH_REQUEST(pubkey)
> 	(4) S->C:	USERAUTH_SUCCESS/FAILURE
> 
> the draft reads:
> 
> >	The server MUST acknowledge
> >	any failed requests with a SSH_MSG_USERAUTH_FAILURE message.
> 
> I guess this does _not_ mean that after message (3) the
> server has to send a USERAUTH_FAILURE for message (1),
> since this message has been handled by (2).
> However, on receiving (3) the state associated with "kbdint"
> must be abandonned.

Attached is a patch to correctly abandon authentications in PAM.  It is
against 3.0.2p1.  It basically completes the PAM auth (unsuccessfully)
and then restarts the new authentication.  In this patch, only PAM can
abandon, but the method is trivially implementable by the other auth types.
If desired, I can supply patches for the other auth methods also.

I added a packet_rewind() function to packet.c, needed to support this.
You may think it nasty at first glance, but really in the light of how
PAM works, it's the best solution (IMHO).  If PAM actually called the
new auth method, there'd be some nasty bits to deal with -- eg, you can't
know how many times the conversation function is going to be called.

Also attached is a patch to the client to test that the server does
the right thing.  The patched client sends a kbdint auth request, then
sends a password auth request.  The client throws away the INFO_REQUEST
and doesn't expect a failure message for the abandoned authentication.

It works for me on a Linux 2.2.19/glibc-2.1 x86 machine.
/fc

-------------- next part --------------
diff -ur openssh.orig/auth.c openssh/auth.c
--- openssh.orig/auth.c	Thu Jan 10 14:40:45 2002
+++ openssh/auth.c	Thu Jan 10 22:16:13 2002
@@ -202,7 +202,8 @@
 	if (authctxt->postponed)
 		authmsg = "Postponed";
 	else
-		authmsg = authenticated ? "Accepted" : "Failed";
+		authmsg = (authenticated == 1) ? "Accepted" :
+			  (authenticated == 0) ? "Failed" : "Abandoned";
 
 	authlog("%s %s for %s%.100s from %.200s port %d%s",
 	    authmsg,
diff -ur openssh.orig/auth2-pam.c openssh/auth2-pam.c
--- openssh.orig/auth2-pam.c	Thu Jan 10 14:40:45 2002
+++ openssh/auth2-pam.c	Thu Jan 10 23:22:43 2002
@@ -17,12 +17,14 @@
     const struct pam_message **msg, struct pam_response **resp, 
     void *appdata_ptr);
 void input_userauth_info_response_pam(int type, int plen, void *ctxt);
+static void pam_input_userauth_request(int, int, void *);
+extern void input_userauth_request(int, int, void *);
 
 struct {
-	int finished, num_received, num_expected;
+	int finished, num_received, num_expected, abandon;
 	int *prompts;
 	struct pam_response *responses;
-} context_pam2 = {0, 0, 0, NULL};
+} context_pam2 = {0, 0, 0, 0, NULL};
 
 static struct pam_conv conv2 = {
 	do_pam_conversation_kbd_int,
@@ -32,20 +34,16 @@
 int
 auth2_pam(Authctxt *authctxt)
 {
-	int retval = -1;
-
 	if (authctxt->user == NULL)
 		fatal("auth2_pam: internal error: no user");
 
 	conv2.appdata_ptr = authctxt;
 	do_pam_set_conv(&conv2);
+	context_pam2.abandon = 0;
 
-	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
-	    &input_userauth_info_response_pam);
-	retval = (do_pam_authenticate(0) == PAM_SUCCESS);
-	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
-
-	return retval;
+	/* 3-way return value: 1 = success, 0 = failure, -1 = quiet failure */
+	return (do_pam_authenticate(0) == PAM_SUCCESS) ? 1 :
+		context_pam2.abandon;
 }
 
 static int
@@ -62,6 +60,9 @@
 	context_pam2.responses = xmalloc(sizeof(struct pam_response) * num_msg);
 	memset(context_pam2.responses, 0, sizeof(struct pam_response) * num_msg);
 
+	if (context_pam2.abandon == -1)
+		return PAM_CONV_ERR;
+
 	text = NULL;
 	for (i = 0, context_pam2.num_expected = 0; i < num_msg; i++) {
 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
@@ -113,12 +114,18 @@
 	 * the client *should* be in lock-step with us, so the loop should
 	 * only be traversed once.
 	 */
+	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
+		     &input_userauth_info_response_pam);
+	dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &pam_input_userauth_request);
 	while(context_pam2.finished == 0) {
 		done = 1;
+debug("doing pam dispatch");
 		dispatch_run(DISPATCH_BLOCK, &done, appdata_ptr);
 		if(context_pam2.finished == 0)
 			debug("extra packet during conversation");
 	}
+	dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
+	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
 
 	if(context_pam2.num_received == context_pam2.num_expected) {
 		*resp = context_pam2.responses;
@@ -153,6 +160,20 @@
 	context_pam2.finished = 1;
 
 	packet_done();
+}
+
+
+/* PAM version of input_userauth_request(), needed so we abandon correctly. */
+
+static void
+pam_input_userauth_request(int type, int plen, void *ctxt)
+{
+debug("got pam userauth");
+	/* Tell PAM to abandon. */
+	context_pam2.abandon = -1;
+	context_pam2.finished = 1;
+	/* Tell packet_read() to restart. */
+	packet_rewind(type);
 }
 
 #endif
diff -ur openssh.orig/auth2.c openssh/auth2.c
--- openssh.orig/auth2.c	Thu Jan 10 14:40:45 2002
+++ openssh/auth2.c	Thu Jan 10 23:20:14 2002
@@ -70,7 +70,7 @@
 /* protocol */
 
 static void input_service_request(int, int, void *);
-static void input_userauth_request(int, int, void *);
+void input_userauth_request(int, int, void *);
 static void protocol_error(int, int, void *);
 
 /* helper */
@@ -172,7 +172,7 @@
 	xfree(service);
 }
 
-static void
+void
 input_userauth_request(int type, int plen, void *ctxt)
 {
 	Authctxt *authctxt = ctxt;
@@ -252,13 +252,13 @@
 		    authctxt->user);
 
 	/* Special handling for root */
-	if (authenticated && authctxt->pw->pw_uid == 0 &&
+	if ((authenticated > 0) && authctxt->pw->pw_uid == 0 &&
 	    !auth_root_allowed(method))
 		authenticated = 0;
 
 #ifdef USE_PAM
-	if (authenticated && authctxt->user && !do_pam_account(authctxt->user,
-	    NULL))
+	if ((authenticated > 0) && authctxt->user &&
+	    !do_pam_account(authctxt->user, NULL))
 		authenticated = 0;
 #endif /* USE_PAM */
 
@@ -278,6 +278,7 @@
 		/* now we can break out */
 		authctxt->success = 1;
 	} else {
+		/* This counts abaondoned authentications as failures. */
 		if (authctxt->failures++ > AUTH_FAIL_MAX) {
 #ifdef WITH_AIXAUTHENTICATE
 			loginfailed(authctxt->user,
@@ -286,13 +287,16 @@
 #endif /* WITH_AIXAUTHENTICATE */
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
-		methods = authmethods_get();
-		packet_start(SSH2_MSG_USERAUTH_FAILURE);
-		packet_put_cstring(methods);
-		packet_put_char(0);	/* XXX partial success, unused */
-		packet_send();
-		packet_write_wait();
-		xfree(methods);
+		/* Don't send a failure message if auth was abandoned. */
+		if (authenticated == 0) {
+			methods = authmethods_get();
+			packet_start(SSH2_MSG_USERAUTH_FAILURE);
+			packet_put_cstring(methods);
+			packet_put_char(0); /* XXX partial success, unused */
+			packet_send();
+			packet_write_wait();
+			xfree(methods);
+		}
 	}
 }
 
diff -ur openssh.orig/packet.c openssh/packet.c
--- openssh.orig/packet.c	Thu Jan 10 14:40:45 2002
+++ openssh/packet.c	Thu Jan 10 23:26:37 2002
@@ -603,6 +603,8 @@
 	DBG(debug("packet_send done"));
 }
 
+static int rewind_type = -1; /* see packet_read() and packet_rewind() */
+
 /*
  * Waits until a packet has been received, and returns its type.  Note that
  * no other data is processed until this returns, so this function should not
@@ -617,6 +619,19 @@
 	char buf[8192];
 	DBG(debug("packet_read()"));
 
+	/* "rewind" if requested */
+	if (rewind_type != -1) {
+		type = rewind_type;
+		rewind_type = -1;
+		*payload_len_ptr = buffer_len(&incoming_packet);
+debug("rewind type %u, len %u", rewind_type, *payload_len_ptr);
+#ifdef PACKET_DEBUG
+		fprintf(stderr, "re-read/plain[%d]:\r\n", type);
+		buffer_dump(&incoming_packet);
+#endif
+		return type;
+	}
+
 	setp = (fd_set *)xmalloc(howmany(connection_in+1, NFDBITS) *
 	    sizeof(fd_mask));
 
@@ -959,6 +974,20 @@
 packet_process_incoming(const char *buf, u_int len)
 {
 	buffer_append(&input, buf, len);
+}
+
+/*
+ * Places a packet back into the input stream.  The next call to
+ * packet_read() will "re-read" this packet.  The caller must not have
+ * processed the packet (ie, no calls to packet_get_string(), etc.).
+ * This is probably only useful during ssh2 authentication, to abandon an
+ * authentication request if a new one is started.
+ */
+
+void
+packet_rewind(int type)
+{
+	rewind_type = type;
 }
 
 /* Returns a character from the packet. */
diff -ur openssh.orig/packet.h openssh/packet.h
--- openssh.orig/packet.h	Thu Jan 10 14:40:45 2002
+++ openssh/packet.h	Thu Jan 10 22:19:40 2002
@@ -44,6 +44,7 @@
 void     packet_read_expect(int *payload_len_ptr, int type);
 int      packet_read_poll(int *packet_len_ptr);
 void     packet_process_incoming(const char *buf, u_int len);
+void     packet_rewind(int type);
 
 u_int	 packet_get_char(void);
 u_int	 packet_get_int(void);

-------------- next part --------------
diff -ur openssh.orig/sshconnect2.c openssh/sshconnect2.c
--- openssh.orig/sshconnect2.c	Thu Jan 10 14:40:45 2002
+++ openssh/sshconnect2.c	Thu Jan 10 23:44:46 2002
@@ -455,13 +455,14 @@
 	packet_start(SSH2_MSG_USERAUTH_REQUEST);
 	packet_put_cstring(authctxt->server_user);
 	packet_put_cstring(authctxt->service);
-	packet_put_cstring(authctxt->method->name);
+	packet_put_cstring("password");
 	packet_put_char(0);
 	packet_put_cstring(password);
 	memset(password, 0, strlen(password));
 	xfree(password);
 	packet_add_padding(64);
 	packet_send();
+packet_write_wait();
 	return 1;
 }
 
@@ -759,9 +760,13 @@
 	packet_put_cstring(options.kbd_interactive_devices ?
 	    options.kbd_interactive_devices : "");
 	packet_send();
+packet_write_wait();
 
 	dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
+/*
 	return 1;
+*/
+	return userauth_passwd(authctxt);
 }
 
 /*
@@ -777,6 +782,8 @@
 
 	debug2("input_userauth_info_req");
 
+return;
+
 	if (authctxt == NULL)
 		fatal("input_userauth_info_req: no authentication context");
 


More information about the openssh-unix-dev mailing list