xref: /titanic_50/usr/src/cmd/ssh/sshd/auth2-pam.c (revision 6f786ace10b9c0c7c5515e525fb660fbccfda6a3)
17c478bd9Sstevel@tonic-gate /*
2*6f786aceSNobutomo Nakano  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate #include "includes.h"
77c478bd9Sstevel@tonic-gate 
87c478bd9Sstevel@tonic-gate RCSID("$Id: auth2-pam.c,v 1.14 2002/06/28 16:48:12 mouring Exp $");
97c478bd9Sstevel@tonic-gate 
107c478bd9Sstevel@tonic-gate #ifdef USE_PAM
117c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
127c478bd9Sstevel@tonic-gate 
137c478bd9Sstevel@tonic-gate #include "ssh.h"
147c478bd9Sstevel@tonic-gate #include "ssh2.h"
157c478bd9Sstevel@tonic-gate #include "auth.h"
167c478bd9Sstevel@tonic-gate #include "auth-pam.h"
177c478bd9Sstevel@tonic-gate #include "auth-options.h"
187c478bd9Sstevel@tonic-gate #include "packet.h"
197c478bd9Sstevel@tonic-gate #include "xmalloc.h"
207c478bd9Sstevel@tonic-gate #include "dispatch.h"
217c478bd9Sstevel@tonic-gate #include "canohost.h"
227c478bd9Sstevel@tonic-gate #include "log.h"
237c478bd9Sstevel@tonic-gate #include "servconf.h"
247c478bd9Sstevel@tonic-gate #include "misc.h"
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #ifdef HAVE_BSM
277c478bd9Sstevel@tonic-gate #include "bsmaudit.h"
287c478bd9Sstevel@tonic-gate #endif /* HAVE_BSM */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate extern u_int utmp_len;
317c478bd9Sstevel@tonic-gate extern ServerOptions options;
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate extern Authmethod method_kbdint;
347c478bd9Sstevel@tonic-gate extern Authmethod method_passwd;
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #define SSHD_PAM_KBDINT_SVC "sshd-kbdint"
37264ad50bSPeter Shoults /* Maximum attempts for changing expired password */
38264ad50bSPeter Shoults #define DEF_ATTEMPTS 3
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate static int do_pam_conv_kbd_int(int num_msg,
417c478bd9Sstevel@tonic-gate     struct pam_message **msg, struct pam_response **resp,
427c478bd9Sstevel@tonic-gate     void *appdata_ptr);
437c478bd9Sstevel@tonic-gate static void input_userauth_info_response_pam(int type,
447c478bd9Sstevel@tonic-gate 					     u_int32_t seqnr,
457c478bd9Sstevel@tonic-gate 					     void *ctxt);
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate static struct pam_conv conv2 = {
487c478bd9Sstevel@tonic-gate 	do_pam_conv_kbd_int,
497c478bd9Sstevel@tonic-gate 	NULL,
507c478bd9Sstevel@tonic-gate };
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate static void do_pam_kbdint_cleanup(pam_handle_t *pamh);
537c478bd9Sstevel@tonic-gate static void do_pam_kbdint(Authctxt *authctxt);
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate void
auth2_pam(Authctxt * authctxt)567c478bd9Sstevel@tonic-gate auth2_pam(Authctxt *authctxt)
577c478bd9Sstevel@tonic-gate {
587c478bd9Sstevel@tonic-gate 	if (authctxt->user == NULL)
597c478bd9Sstevel@tonic-gate 		fatal("auth2_pam: internal error: no user");
607c478bd9Sstevel@tonic-gate 	if (authctxt->method == NULL)
617c478bd9Sstevel@tonic-gate 		fatal("auth2_pam: internal error: no method");
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate 	conv2.appdata_ptr = authctxt;
647c478bd9Sstevel@tonic-gate 	new_start_pam(authctxt, &conv2);
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 	authctxt->method->method_data = NULL; /* freed in the conv func */
677c478bd9Sstevel@tonic-gate 	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
687c478bd9Sstevel@tonic-gate 	    &input_userauth_info_response_pam);
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	/*
717c478bd9Sstevel@tonic-gate 	 * Since password userauth and keyboard-interactive userauth
727c478bd9Sstevel@tonic-gate 	 * both use PAM, and since keyboard-interactive is so much
737c478bd9Sstevel@tonic-gate 	 * better than password userauth, we should not allow the user
747c478bd9Sstevel@tonic-gate 	 * to try password userauth after trying keyboard-interactive.
757c478bd9Sstevel@tonic-gate 	 */
767c478bd9Sstevel@tonic-gate 	if (method_passwd.enabled)
777c478bd9Sstevel@tonic-gate 		*method_passwd.enabled = 0;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	do_pam_kbdint(authctxt);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
827c478bd9Sstevel@tonic-gate }
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate static void
do_pam_kbdint(Authctxt * authctxt)857c478bd9Sstevel@tonic-gate do_pam_kbdint(Authctxt *authctxt)
867c478bd9Sstevel@tonic-gate {
877c478bd9Sstevel@tonic-gate 	int		 retval, retval2;
887c478bd9Sstevel@tonic-gate 	pam_handle_t	*pamh = authctxt->pam->h;
897c478bd9Sstevel@tonic-gate 	const char	*where = "authenticating";
90f69a4b3cSvk199839 	char		*text = NULL;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	debug2("Calling pam_authenticate()");
93b2133dc7Sdarrenm 	retval = pam_authenticate(pamh,
94b2133dc7Sdarrenm 	    options.permit_empty_passwd ? 0 :
95b2133dc7Sdarrenm 	    PAM_DISALLOW_NULL_AUTHTOK);
96b2133dc7Sdarrenm 
97b2133dc7Sdarrenm 	if (retval != PAM_SUCCESS)
987c478bd9Sstevel@tonic-gate 		goto cleanup;
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	debug2("kbd-int: pam_authenticate() succeeded");
1017c478bd9Sstevel@tonic-gate 	where = "authorizing";
1027c478bd9Sstevel@tonic-gate 	retval = pam_acct_mgmt(pamh, 0);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	if (retval == PAM_NEW_AUTHTOK_REQD) {
1057c478bd9Sstevel@tonic-gate 		if (authctxt->valid && authctxt->pw != NULL) {
106f69a4b3cSvk199839 			/* send password expiration warning */
107f69a4b3cSvk199839 			message_cat(&text,
108f69a4b3cSvk199839 			    gettext("Warning: Your password has expired,"
109f69a4b3cSvk199839 			    " please change it now."));
110f69a4b3cSvk199839 			packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
111f69a4b3cSvk199839 			packet_put_cstring("");		/* name */
112*6f786aceSNobutomo Nakano 			packet_put_utf8_cstring(text);	/* instructions */
113f69a4b3cSvk199839 			packet_put_cstring("");		/* language, unused */
114f69a4b3cSvk199839 			packet_put_int(0);
115f69a4b3cSvk199839 			packet_send();
116f69a4b3cSvk199839 			packet_write_wait();
117f69a4b3cSvk199839 			debug("expiration message sent");
118f69a4b3cSvk199839 			if (text)
119f69a4b3cSvk199839 				xfree(text);
120f69a4b3cSvk199839 			/*
121f69a4b3cSvk199839 			 * wait for the response so it does not mix
122f69a4b3cSvk199839 			 * with the upcoming PAM conversation
123f69a4b3cSvk199839 			 */
124f69a4b3cSvk199839 			packet_read_expect(SSH2_MSG_USERAUTH_INFO_RESPONSE);
1257c478bd9Sstevel@tonic-gate 			/*
1267c478bd9Sstevel@tonic-gate 			 * Can't use temporarily_use_uid() and restore_uid()
1277c478bd9Sstevel@tonic-gate 			 * here because we need (euid == 0 && ruid == pw_uid)
1287c478bd9Sstevel@tonic-gate 			 * whereas temporarily_use_uid() arranges for
1297c478bd9Sstevel@tonic-gate 			 * (suid = 0 && euid == pw_uid && ruid == pw_uid).
1307c478bd9Sstevel@tonic-gate 			 */
1317c478bd9Sstevel@tonic-gate 			(void) setreuid(authctxt->pw->pw_uid, -1);
1327c478bd9Sstevel@tonic-gate 			debug2("kbd-int: changing expired password");
1337c478bd9Sstevel@tonic-gate 			where = "changing authentication tokens (password)";
134264ad50bSPeter Shoults 			/*
135264ad50bSPeter Shoults 			 * Depending on error returned from pam_chauthtok, we
136264ad50bSPeter Shoults 			 * need to try to change password a few times before
137264ad50bSPeter Shoults 			 * we error out and return.
138264ad50bSPeter Shoults 			 */
139264ad50bSPeter Shoults 			int tries = 0;
140264ad50bSPeter Shoults 			while ((retval = pam_chauthtok(pamh,
141264ad50bSPeter Shoults 			    PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) {
142264ad50bSPeter Shoults 				if (tries++ < DEF_ATTEMPTS) {
143264ad50bSPeter Shoults 					if ((retval == PAM_AUTHTOK_ERR) ||
144264ad50bSPeter Shoults 					    (retval == PAM_TRY_AGAIN)) {
145264ad50bSPeter Shoults 						continue;
146264ad50bSPeter Shoults 					}
147264ad50bSPeter Shoults 				}
148264ad50bSPeter Shoults 				break;
149264ad50bSPeter Shoults 			}
1507c478bd9Sstevel@tonic-gate 			audit_sshd_chauthtok(retval, authctxt->pw->pw_uid,
1517c478bd9Sstevel@tonic-gate 				authctxt->pw->pw_gid);
1527c478bd9Sstevel@tonic-gate 			(void) setreuid(0, -1);
1537c478bd9Sstevel@tonic-gate 		} else {
1547c478bd9Sstevel@tonic-gate 			retval = PAM_PERM_DENIED;
1557c478bd9Sstevel@tonic-gate 		}
1567c478bd9Sstevel@tonic-gate 	}
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	if (retval != PAM_SUCCESS)
1597c478bd9Sstevel@tonic-gate 		goto cleanup;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	retval = finish_userauth_do_pam(authctxt);
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if (retval != PAM_SUCCESS)
1667c478bd9Sstevel@tonic-gate 		goto cleanup;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	/*
1697c478bd9Sstevel@tonic-gate 	 * PAM handle stays around so we can call pam_close_session()
1707c478bd9Sstevel@tonic-gate 	 * on it later.
1717c478bd9Sstevel@tonic-gate 	 */
1727c478bd9Sstevel@tonic-gate 	authctxt->method->authenticated = 1;
1737c478bd9Sstevel@tonic-gate 	debug2("kbd-int: success (pam->state == %x)", authctxt->pam->state);
1747c478bd9Sstevel@tonic-gate 	return;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate cleanup:
1777c478bd9Sstevel@tonic-gate 	/*
1787c478bd9Sstevel@tonic-gate 	 * Check for abandonment and cleanup.  When kbdint is abandoned
1797c478bd9Sstevel@tonic-gate 	 * authctxt->pam->h is NULLed and by this point a new handle may
1807c478bd9Sstevel@tonic-gate 	 * be allocated.
1817c478bd9Sstevel@tonic-gate 	 */
1827c478bd9Sstevel@tonic-gate 	if (authctxt->pam->h != pamh) {
1837c478bd9Sstevel@tonic-gate 		log("Keyboard-interactive (PAM) userauth abandoned "
1847c478bd9Sstevel@tonic-gate 		    "while %s", where);
1857c478bd9Sstevel@tonic-gate 		if ((retval2 = pam_end(pamh, retval)) != PAM_SUCCESS) {
1867c478bd9Sstevel@tonic-gate 			log("Cannot close PAM handle after "
1877c478bd9Sstevel@tonic-gate 			    "kbd-int userauth abandonment[%d]: %.200s",
1887c478bd9Sstevel@tonic-gate 			    retval2, PAM_STRERROR(pamh, retval2));
1897c478bd9Sstevel@tonic-gate 		}
1907c478bd9Sstevel@tonic-gate 		authctxt->method->abandoned = 1;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 		/*
1937c478bd9Sstevel@tonic-gate 		 * Avoid double counting; these are incremented in
1947c478bd9Sstevel@tonic-gate 		 * kbdint_pam_abandon() so that they reflect the correct
1957c478bd9Sstevel@tonic-gate 		 * count when userauth_finish() is called before
1967c478bd9Sstevel@tonic-gate 		 * unwinding the dispatch_run() loop, but they are
1977c478bd9Sstevel@tonic-gate 		 * incremented again in input_userauth_request() when
1987c478bd9Sstevel@tonic-gate 		 * the loop is unwound, right here.
1997c478bd9Sstevel@tonic-gate 		 */
2007c478bd9Sstevel@tonic-gate 		if (authctxt->method->abandons)
2017c478bd9Sstevel@tonic-gate 			authctxt->method->abandons--;
2027c478bd9Sstevel@tonic-gate 		if (authctxt->method->attempts)
2037c478bd9Sstevel@tonic-gate 			authctxt->method->attempts--;
2047c478bd9Sstevel@tonic-gate 	}
2057c478bd9Sstevel@tonic-gate 	else {
2067c478bd9Sstevel@tonic-gate 		/* Save error value for pam_end() */
2077c478bd9Sstevel@tonic-gate 		authctxt->pam->last_pam_retval = retval;
2087c478bd9Sstevel@tonic-gate 		log("Keyboard-interactive (PAM) userauth failed[%d] "
2097c478bd9Sstevel@tonic-gate 		    "while %s: %.200s", retval, where,
2107c478bd9Sstevel@tonic-gate 		    PAM_STRERROR(pamh, retval));
2117c478bd9Sstevel@tonic-gate 		/* pam handle can be reused elsewhere, so no pam_end() here */
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	return;
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate static int
do_pam_conv_kbd_int(int num_msg,struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)2187c478bd9Sstevel@tonic-gate do_pam_conv_kbd_int(int num_msg, struct pam_message **msg,
2197c478bd9Sstevel@tonic-gate     struct pam_response **resp, void *appdata_ptr)
2207c478bd9Sstevel@tonic-gate {
2217c478bd9Sstevel@tonic-gate 	int i, j;
2227c478bd9Sstevel@tonic-gate 	char *text;
2237c478bd9Sstevel@tonic-gate 	Convctxt *conv_ctxt;
2247c478bd9Sstevel@tonic-gate 	Authctxt *authctxt = (Authctxt *)appdata_ptr;
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	if (!authctxt || !authctxt->method) {
2277c478bd9Sstevel@tonic-gate 		debug("Missing state during PAM conversation");
2287c478bd9Sstevel@tonic-gate 		return PAM_CONV_ERR;
2297c478bd9Sstevel@tonic-gate 	}
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	conv_ctxt = xmalloc(sizeof(Convctxt));
2327c478bd9Sstevel@tonic-gate 	(void) memset(conv_ctxt, 0, sizeof(Convctxt));
2337c478bd9Sstevel@tonic-gate 	conv_ctxt->finished = 0;
2347c478bd9Sstevel@tonic-gate 	conv_ctxt->num_received = 0;
2357c478bd9Sstevel@tonic-gate 	conv_ctxt->num_expected = 0;
2367c478bd9Sstevel@tonic-gate 	conv_ctxt->prompts = xmalloc(sizeof(int) * num_msg);
2377c478bd9Sstevel@tonic-gate 	conv_ctxt->responses = xmalloc(sizeof(struct pam_response) * num_msg);
2387c478bd9Sstevel@tonic-gate 	(void) memset(conv_ctxt->responses, 0, sizeof(struct pam_response) * num_msg);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	text = NULL;
2417c478bd9Sstevel@tonic-gate 	for (i = 0, conv_ctxt->num_expected = 0; i < num_msg; i++) {
2427c478bd9Sstevel@tonic-gate 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
2437c478bd9Sstevel@tonic-gate 		switch (style) {
2447c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
2457c478bd9Sstevel@tonic-gate 			debug2("PAM echo on prompt: %s",
2467c478bd9Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2477c478bd9Sstevel@tonic-gate 			conv_ctxt->num_expected++;
2487c478bd9Sstevel@tonic-gate 			break;
2497c478bd9Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
2507c478bd9Sstevel@tonic-gate 			debug2("PAM echo off prompt: %s",
2517c478bd9Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2527c478bd9Sstevel@tonic-gate 			conv_ctxt->num_expected++;
2537c478bd9Sstevel@tonic-gate 			break;
2547c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
2557c478bd9Sstevel@tonic-gate 			debug2("PAM text info prompt: %s",
2567c478bd9Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2577c478bd9Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2587c478bd9Sstevel@tonic-gate 			break;
2597c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
2607c478bd9Sstevel@tonic-gate 			debug2("PAM error prompt: %s",
2617c478bd9Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2627c478bd9Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2637c478bd9Sstevel@tonic-gate 			break;
2647c478bd9Sstevel@tonic-gate 		default:
2657c478bd9Sstevel@tonic-gate 			/* Capture all these messages to be sent at once */
2667c478bd9Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2677c478bd9Sstevel@tonic-gate 			break;
2687c478bd9Sstevel@tonic-gate 		}
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	if (conv_ctxt->num_expected == 0 && text == NULL) {
2729a8058b5Sjp161948 		xfree(conv_ctxt->prompts);
2739a8058b5Sjp161948 		xfree(conv_ctxt->responses);
2747c478bd9Sstevel@tonic-gate 		xfree(conv_ctxt);
2757c478bd9Sstevel@tonic-gate 		return PAM_SUCCESS;
2767c478bd9Sstevel@tonic-gate 	}
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	authctxt->method->method_data = (void *) conv_ctxt;
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
2817c478bd9Sstevel@tonic-gate 	packet_put_cstring("");	/* Name */
282*6f786aceSNobutomo Nakano 	packet_put_utf8_cstring(text ? text : "");	/* Instructions */
2837c478bd9Sstevel@tonic-gate 	packet_put_cstring("");	/* Language */
2847c478bd9Sstevel@tonic-gate 	packet_put_int(conv_ctxt->num_expected);
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	if (text)
2877c478bd9Sstevel@tonic-gate 		xfree(text);
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	for (i = 0, j = 0; i < num_msg; i++) {
2907c478bd9Sstevel@tonic-gate 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 		/* Skip messages which don't need a reply */
2937c478bd9Sstevel@tonic-gate 		if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF)
2947c478bd9Sstevel@tonic-gate 			continue;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 		conv_ctxt->prompts[j++] = i;
297*6f786aceSNobutomo Nakano 		packet_put_utf8_cstring(PAM_MSG_MEMBER(msg, i, msg));
2987c478bd9Sstevel@tonic-gate 		packet_put_char(style == PAM_PROMPT_ECHO_ON);
2997c478bd9Sstevel@tonic-gate 	}
3007c478bd9Sstevel@tonic-gate 	packet_send();
3017c478bd9Sstevel@tonic-gate 	packet_write_wait();
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * Here the dispatch_run() loop is nested.  It should be unwound
3057c478bd9Sstevel@tonic-gate 	 * if keyboard-interactive userauth is abandoned (or restarted;
3067c478bd9Sstevel@tonic-gate 	 * same thing).
3077c478bd9Sstevel@tonic-gate 	 *
3087c478bd9Sstevel@tonic-gate 	 * The condition for breaking out of the nested dispatch_run() loop is
3097c478bd9Sstevel@tonic-gate 	 *     ((got kbd-int info reponse) || (kbd-int abandoned))
3107c478bd9Sstevel@tonic-gate 	 *
3117c478bd9Sstevel@tonic-gate 	 * conv_ctxt->finished is set in either of those cases.
3127c478bd9Sstevel@tonic-gate 	 *
3137c478bd9Sstevel@tonic-gate 	 * When abandonment is detected the conv_ctxt->finished is set as
3147c478bd9Sstevel@tonic-gate 	 * is conv_ctxt->abandoned, causing this function to signal
3157c478bd9Sstevel@tonic-gate 	 * userauth nested dispatch_run() loop unwinding and to return
3167c478bd9Sstevel@tonic-gate 	 * PAM_CONV_ERR;
3177c478bd9Sstevel@tonic-gate 	 */
3187c478bd9Sstevel@tonic-gate 	debug2("Nesting dispatch_run loop");
3197c478bd9Sstevel@tonic-gate 	dispatch_run(DISPATCH_BLOCK, &conv_ctxt->finished, appdata_ptr);
3207c478bd9Sstevel@tonic-gate 	debug2("Nested dispatch_run loop exited");
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	if (conv_ctxt->abandoned) {
3237c478bd9Sstevel@tonic-gate 		authctxt->unwind_dispatch_loop = 1;
3249a8058b5Sjp161948 		xfree(conv_ctxt->prompts);
3259a8058b5Sjp161948 		xfree(conv_ctxt->responses);
3267c478bd9Sstevel@tonic-gate 		xfree(conv_ctxt);
3277c478bd9Sstevel@tonic-gate 		debug("PAM conv function returns PAM_CONV_ERR");
3287c478bd9Sstevel@tonic-gate 		return PAM_CONV_ERR;
3297c478bd9Sstevel@tonic-gate 	}
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	if (conv_ctxt->num_received == conv_ctxt->num_expected) {
3327c478bd9Sstevel@tonic-gate 		*resp = conv_ctxt->responses;
3339a8058b5Sjp161948 		xfree(conv_ctxt->prompts);
3347c478bd9Sstevel@tonic-gate 		xfree(conv_ctxt);
3357c478bd9Sstevel@tonic-gate 		debug("PAM conv function returns PAM_SUCCESS");
3367c478bd9Sstevel@tonic-gate 		return PAM_SUCCESS;
3377c478bd9Sstevel@tonic-gate 	}
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	debug("PAM conv function returns PAM_CONV_ERR");
3409a8058b5Sjp161948 	xfree(conv_ctxt->prompts);
3419a8058b5Sjp161948 	xfree(conv_ctxt->responses);
3427c478bd9Sstevel@tonic-gate 	xfree(conv_ctxt);
3437c478bd9Sstevel@tonic-gate 	return PAM_CONV_ERR;
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate static void
input_userauth_info_response_pam(int type,u_int32_t seqnr,void * ctxt)3477c478bd9Sstevel@tonic-gate input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt)
3487c478bd9Sstevel@tonic-gate {
3497c478bd9Sstevel@tonic-gate 	Authctxt *authctxt = ctxt;
3507c478bd9Sstevel@tonic-gate 	Convctxt *conv_ctxt;
3517c478bd9Sstevel@tonic-gate 	unsigned int nresp = 0, rlen = 0, i = 0;
3527c478bd9Sstevel@tonic-gate 	char *resp;
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	if (authctxt == NULL)
3557c478bd9Sstevel@tonic-gate 		fatal("input_userauth_info_response_pam: no authentication context");
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	/* Check for spurious/unexpected info response */
3587c478bd9Sstevel@tonic-gate 	if (method_kbdint.method_data == NULL) {
3597c478bd9Sstevel@tonic-gate 		debug("input_userauth_info_response_pam: no method context");
3607c478bd9Sstevel@tonic-gate 		return;
3617c478bd9Sstevel@tonic-gate 	}
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	conv_ctxt = (Convctxt *) method_kbdint.method_data;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	nresp = packet_get_int();	/* Number of responses. */
3667c478bd9Sstevel@tonic-gate 	debug("got %d responses", nresp);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate #if 0
3707c478bd9Sstevel@tonic-gate 	if (nresp != conv_ctxt->num_expected)
3717c478bd9Sstevel@tonic-gate 		fatal("%s: Received incorrect number of responses "
3727c478bd9Sstevel@tonic-gate 		    "(expected %d, received %u)", __func__,
3737c478bd9Sstevel@tonic-gate 		    conv_ctxt->num_expected, nresp);
3747c478bd9Sstevel@tonic-gate #endif
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	if (nresp > 100)
3777c478bd9Sstevel@tonic-gate 		fatal("%s: too many replies", __func__);
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	for (i = 0; i < nresp && i < conv_ctxt->num_expected ; i++) {
3807c478bd9Sstevel@tonic-gate 		int j = conv_ctxt->prompts[i];
3817c478bd9Sstevel@tonic-gate 
382*6f786aceSNobutomo Nakano 		/*
383*6f786aceSNobutomo Nakano 		 * We assume that ASCII charset is used for password
384*6f786aceSNobutomo Nakano 		 * although the protocol requires UTF-8 encoding for the
385*6f786aceSNobutomo Nakano 		 * password string. Therefore, we don't perform code
386*6f786aceSNobutomo Nakano 		 * conversion for the string.
387*6f786aceSNobutomo Nakano 		 */
3887c478bd9Sstevel@tonic-gate 		resp = packet_get_string(&rlen);
3897c478bd9Sstevel@tonic-gate 		if (i < conv_ctxt->num_expected) {
3907c478bd9Sstevel@tonic-gate 			conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS;
3917c478bd9Sstevel@tonic-gate 			conv_ctxt->responses[j].resp = xstrdup(resp);
3927c478bd9Sstevel@tonic-gate 			conv_ctxt->num_received++;
3937c478bd9Sstevel@tonic-gate 		}
3947c478bd9Sstevel@tonic-gate 		xfree(resp);
3957c478bd9Sstevel@tonic-gate 	}
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	if (nresp < conv_ctxt->num_expected)
398f69a4b3cSvk199839 		fatal("%s: too few replies (%d < %d)", __func__,
399f69a4b3cSvk199839 		    nresp, conv_ctxt->num_expected);
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	/* XXX - This could make a covert channel... */
4027c478bd9Sstevel@tonic-gate 	if (nresp > conv_ctxt->num_expected)
4037c478bd9Sstevel@tonic-gate 		debug("Ignoring additional PAM replies");
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	conv_ctxt->finished = 1;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	packet_check_eom();
4087c478bd9Sstevel@tonic-gate }
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate #if 0
4117c478bd9Sstevel@tonic-gate int
4127c478bd9Sstevel@tonic-gate kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method)
4137c478bd9Sstevel@tonic-gate {
4147c478bd9Sstevel@tonic-gate 	if (!method)
4157c478bd9Sstevel@tonic-gate 		return 0; /* fatal(), really; it'll happen somewhere else */
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	if (!method->method_data)
4187c478bd9Sstevel@tonic-gate 		return 0;
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	return 1;
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate #endif
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate void
kbdint_pam_abandon(Authctxt * authctxt,Authmethod * method)4257c478bd9Sstevel@tonic-gate kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method)
4267c478bd9Sstevel@tonic-gate {
4277c478bd9Sstevel@tonic-gate 	Convctxt *conv_ctxt;
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	/*
4307c478bd9Sstevel@tonic-gate 	 * But, if it ever becomes desirable and possible to support
4317c478bd9Sstevel@tonic-gate 	 * kbd-int userauth abandonment, here's what must be done.
4327c478bd9Sstevel@tonic-gate 	 */
4337c478bd9Sstevel@tonic-gate 	if (!method)
4347c478bd9Sstevel@tonic-gate 		return;
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	if (!method->method_data)
4377c478bd9Sstevel@tonic-gate 		return;
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	conv_ctxt = (Convctxt *) method->method_data;
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	/* dispatch_run() loop will exit */
4427c478bd9Sstevel@tonic-gate 	conv_ctxt->abandoned = 1;
4437c478bd9Sstevel@tonic-gate 	conv_ctxt->finished = 1;
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	/*
4467c478bd9Sstevel@tonic-gate 	 * The method_data will be free in the corresponding, active
4477c478bd9Sstevel@tonic-gate 	 * conversation function
4487c478bd9Sstevel@tonic-gate 	 */
4497c478bd9Sstevel@tonic-gate 	method->method_data = NULL;
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	/* update counts that can't be updated elsewhere */
4527c478bd9Sstevel@tonic-gate 	method->abandons++;
4537c478bd9Sstevel@tonic-gate 	method->attempts++;
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	/* Finally, we cannot re-use the current current PAM handle */
4567c478bd9Sstevel@tonic-gate 	authctxt->pam->h = NULL;    /* Let the conv function cleanup */
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate #endif
459