xref: /onnv-gate/usr/src/cmd/ssh/sshd/auth2-pam.c (revision 4775:856b1c9ed72b)
10Sstevel@tonic-gate /*
2*4775Svk199839  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate #include "includes.h"
70Sstevel@tonic-gate 
80Sstevel@tonic-gate RCSID("$Id: auth2-pam.c,v 1.14 2002/06/28 16:48:12 mouring Exp $");
90Sstevel@tonic-gate 
100Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
110Sstevel@tonic-gate 
120Sstevel@tonic-gate #ifdef USE_PAM
130Sstevel@tonic-gate #include <security/pam_appl.h>
140Sstevel@tonic-gate 
150Sstevel@tonic-gate #include "ssh.h"
160Sstevel@tonic-gate #include "ssh2.h"
170Sstevel@tonic-gate #include "auth.h"
180Sstevel@tonic-gate #include "auth-pam.h"
190Sstevel@tonic-gate #include "auth-options.h"
200Sstevel@tonic-gate #include "packet.h"
210Sstevel@tonic-gate #include "xmalloc.h"
220Sstevel@tonic-gate #include "dispatch.h"
230Sstevel@tonic-gate #include "canohost.h"
240Sstevel@tonic-gate #include "log.h"
250Sstevel@tonic-gate #include "servconf.h"
260Sstevel@tonic-gate #include "monitor_wrap.h"
270Sstevel@tonic-gate #include "misc.h"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #ifdef HAVE_BSM
300Sstevel@tonic-gate #include "bsmaudit.h"
310Sstevel@tonic-gate #endif /* HAVE_BSM */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate extern u_int utmp_len;
340Sstevel@tonic-gate extern ServerOptions options;
350Sstevel@tonic-gate 
360Sstevel@tonic-gate extern Authmethod method_kbdint;
370Sstevel@tonic-gate extern Authmethod method_passwd;
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #define SSHD_PAM_KBDINT_SVC "sshd-kbdint"
400Sstevel@tonic-gate 
410Sstevel@tonic-gate static int do_pam_conv_kbd_int(int num_msg,
420Sstevel@tonic-gate     struct pam_message **msg, struct pam_response **resp,
430Sstevel@tonic-gate     void *appdata_ptr);
440Sstevel@tonic-gate static void input_userauth_info_response_pam(int type,
450Sstevel@tonic-gate 					     u_int32_t seqnr,
460Sstevel@tonic-gate 					     void *ctxt);
470Sstevel@tonic-gate 
480Sstevel@tonic-gate static struct pam_conv conv2 = {
490Sstevel@tonic-gate 	do_pam_conv_kbd_int,
500Sstevel@tonic-gate 	NULL,
510Sstevel@tonic-gate };
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static void do_pam_kbdint_cleanup(pam_handle_t *pamh);
540Sstevel@tonic-gate static void do_pam_kbdint(Authctxt *authctxt);
550Sstevel@tonic-gate 
560Sstevel@tonic-gate void
570Sstevel@tonic-gate auth2_pam(Authctxt *authctxt)
580Sstevel@tonic-gate {
590Sstevel@tonic-gate 	if (authctxt->user == NULL)
600Sstevel@tonic-gate 		fatal("auth2_pam: internal error: no user");
610Sstevel@tonic-gate 	if (authctxt->method == NULL)
620Sstevel@tonic-gate 		fatal("auth2_pam: internal error: no method");
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	conv2.appdata_ptr = authctxt;
650Sstevel@tonic-gate 	new_start_pam(authctxt, &conv2);
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	authctxt->method->method_data = NULL; /* freed in the conv func */
680Sstevel@tonic-gate 	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
690Sstevel@tonic-gate 	    &input_userauth_info_response_pam);
700Sstevel@tonic-gate 
710Sstevel@tonic-gate 	/*
720Sstevel@tonic-gate 	 * Since password userauth and keyboard-interactive userauth
730Sstevel@tonic-gate 	 * both use PAM, and since keyboard-interactive is so much
740Sstevel@tonic-gate 	 * better than password userauth, we should not allow the user
750Sstevel@tonic-gate 	 * to try password userauth after trying keyboard-interactive.
760Sstevel@tonic-gate 	 */
770Sstevel@tonic-gate 	if (method_passwd.enabled)
780Sstevel@tonic-gate 		*method_passwd.enabled = 0;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	do_pam_kbdint(authctxt);
810Sstevel@tonic-gate 
820Sstevel@tonic-gate 	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
830Sstevel@tonic-gate }
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static void
860Sstevel@tonic-gate do_pam_kbdint(Authctxt *authctxt)
870Sstevel@tonic-gate {
880Sstevel@tonic-gate 	int		 retval, retval2;
890Sstevel@tonic-gate 	pam_handle_t	*pamh = authctxt->pam->h;
900Sstevel@tonic-gate 	const char	*where = "authenticating";
91*4775Svk199839 	char		*text = NULL;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 	debug2("Calling pam_authenticate()");
940Sstevel@tonic-gate 	if ((retval = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
950Sstevel@tonic-gate 		goto cleanup;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	debug2("kbd-int: pam_authenticate() succeeded");
980Sstevel@tonic-gate 	where = "authorizing";
990Sstevel@tonic-gate 	retval = pam_acct_mgmt(pamh, 0);
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	if (retval == PAM_NEW_AUTHTOK_REQD) {
1020Sstevel@tonic-gate 		if (authctxt->valid && authctxt->pw != NULL) {
103*4775Svk199839 			/* send password expiration warning */
104*4775Svk199839 			message_cat(&text,
105*4775Svk199839 			    gettext("Warning: Your password has expired,"
106*4775Svk199839 			    " please change it now."));
107*4775Svk199839 			packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
108*4775Svk199839 			packet_put_cstring("");		/* name */
109*4775Svk199839 			packet_put_cstring(text);	/* instructions */
110*4775Svk199839 			packet_put_cstring("");		/* language, unused */
111*4775Svk199839 			packet_put_int(0);
112*4775Svk199839 			packet_send();
113*4775Svk199839 			packet_write_wait();
114*4775Svk199839 			debug("expiration message sent");
115*4775Svk199839 			if (text)
116*4775Svk199839 				xfree(text);
117*4775Svk199839 			/*
118*4775Svk199839 			 * wait for the response so it does not mix
119*4775Svk199839 			 * with the upcoming PAM conversation
120*4775Svk199839 			 */
121*4775Svk199839 			packet_read_expect(SSH2_MSG_USERAUTH_INFO_RESPONSE);
1220Sstevel@tonic-gate 			/*
1230Sstevel@tonic-gate 			 * Can't use temporarily_use_uid() and restore_uid()
1240Sstevel@tonic-gate 			 * here because we need (euid == 0 && ruid == pw_uid)
1250Sstevel@tonic-gate 			 * whereas temporarily_use_uid() arranges for
1260Sstevel@tonic-gate 			 * (suid = 0 && euid == pw_uid && ruid == pw_uid).
1270Sstevel@tonic-gate 			 */
1280Sstevel@tonic-gate 			(void) setreuid(authctxt->pw->pw_uid, -1);
1290Sstevel@tonic-gate 			debug2("kbd-int: changing expired password");
1300Sstevel@tonic-gate 			where = "changing authentication tokens (password)";
1310Sstevel@tonic-gate 			retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
1320Sstevel@tonic-gate 			audit_sshd_chauthtok(retval, authctxt->pw->pw_uid,
1330Sstevel@tonic-gate 				authctxt->pw->pw_gid);
1340Sstevel@tonic-gate 			(void) setreuid(0, -1);
1350Sstevel@tonic-gate 		} else {
1360Sstevel@tonic-gate 			retval = PAM_PERM_DENIED;
1370Sstevel@tonic-gate 		}
1380Sstevel@tonic-gate 	}
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	if (retval != PAM_SUCCESS)
1410Sstevel@tonic-gate 		goto cleanup;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	retval = finish_userauth_do_pam(authctxt);
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 	if (retval != PAM_SUCCESS)
1480Sstevel@tonic-gate 		goto cleanup;
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	/*
1510Sstevel@tonic-gate 	 * PAM handle stays around so we can call pam_close_session()
1520Sstevel@tonic-gate 	 * on it later.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	authctxt->method->authenticated = 1;
1550Sstevel@tonic-gate 	debug2("kbd-int: success (pam->state == %x)", authctxt->pam->state);
1560Sstevel@tonic-gate 	return;
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate cleanup:
1590Sstevel@tonic-gate 	/*
1600Sstevel@tonic-gate 	 * Check for abandonment and cleanup.  When kbdint is abandoned
1610Sstevel@tonic-gate 	 * authctxt->pam->h is NULLed and by this point a new handle may
1620Sstevel@tonic-gate 	 * be allocated.
1630Sstevel@tonic-gate 	 */
1640Sstevel@tonic-gate 	if (authctxt->pam->h != pamh) {
1650Sstevel@tonic-gate 		log("Keyboard-interactive (PAM) userauth abandoned "
1660Sstevel@tonic-gate 		    "while %s", where);
1670Sstevel@tonic-gate 		if ((retval2 = pam_end(pamh, retval)) != PAM_SUCCESS) {
1680Sstevel@tonic-gate 			log("Cannot close PAM handle after "
1690Sstevel@tonic-gate 			    "kbd-int userauth abandonment[%d]: %.200s",
1700Sstevel@tonic-gate 			    retval2, PAM_STRERROR(pamh, retval2));
1710Sstevel@tonic-gate 		}
1720Sstevel@tonic-gate 		authctxt->method->abandoned = 1;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 		/*
1750Sstevel@tonic-gate 		 * Avoid double counting; these are incremented in
1760Sstevel@tonic-gate 		 * kbdint_pam_abandon() so that they reflect the correct
1770Sstevel@tonic-gate 		 * count when userauth_finish() is called before
1780Sstevel@tonic-gate 		 * unwinding the dispatch_run() loop, but they are
1790Sstevel@tonic-gate 		 * incremented again in input_userauth_request() when
1800Sstevel@tonic-gate 		 * the loop is unwound, right here.
1810Sstevel@tonic-gate 		 */
1820Sstevel@tonic-gate 		if (authctxt->method->abandons)
1830Sstevel@tonic-gate 			authctxt->method->abandons--;
1840Sstevel@tonic-gate 		if (authctxt->method->attempts)
1850Sstevel@tonic-gate 			authctxt->method->attempts--;
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 	else {
1880Sstevel@tonic-gate 		/* Save error value for pam_end() */
1890Sstevel@tonic-gate 		authctxt->pam->last_pam_retval = retval;
1900Sstevel@tonic-gate 		log("Keyboard-interactive (PAM) userauth failed[%d] "
1910Sstevel@tonic-gate 		    "while %s: %.200s", retval, where,
1920Sstevel@tonic-gate 		    PAM_STRERROR(pamh, retval));
1930Sstevel@tonic-gate 		/* pam handle can be reused elsewhere, so no pam_end() here */
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	return;
1970Sstevel@tonic-gate }
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate static int
2000Sstevel@tonic-gate do_pam_conv_kbd_int(int num_msg, struct pam_message **msg,
2010Sstevel@tonic-gate     struct pam_response **resp, void *appdata_ptr)
2020Sstevel@tonic-gate {
2030Sstevel@tonic-gate 	int i, j;
2040Sstevel@tonic-gate 	char *text;
2050Sstevel@tonic-gate 	Convctxt *conv_ctxt;
2060Sstevel@tonic-gate 	Authctxt *authctxt = (Authctxt *)appdata_ptr;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	if (!authctxt || !authctxt->method) {
2090Sstevel@tonic-gate 		debug("Missing state during PAM conversation");
2100Sstevel@tonic-gate 		return PAM_CONV_ERR;
2110Sstevel@tonic-gate 	}
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	conv_ctxt = xmalloc(sizeof(Convctxt));
2140Sstevel@tonic-gate 	(void) memset(conv_ctxt, 0, sizeof(Convctxt));
2150Sstevel@tonic-gate 	conv_ctxt->finished = 0;
2160Sstevel@tonic-gate 	conv_ctxt->num_received = 0;
2170Sstevel@tonic-gate 	conv_ctxt->num_expected = 0;
2180Sstevel@tonic-gate 	conv_ctxt->prompts = xmalloc(sizeof(int) * num_msg);
2190Sstevel@tonic-gate 	conv_ctxt->responses = xmalloc(sizeof(struct pam_response) * num_msg);
2200Sstevel@tonic-gate 	(void) memset(conv_ctxt->responses, 0, sizeof(struct pam_response) * num_msg);
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	text = NULL;
2230Sstevel@tonic-gate 	for (i = 0, conv_ctxt->num_expected = 0; i < num_msg; i++) {
2240Sstevel@tonic-gate 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
2250Sstevel@tonic-gate 		switch (style) {
2260Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
2270Sstevel@tonic-gate 			debug2("PAM echo on prompt: %s",
2280Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2290Sstevel@tonic-gate 			conv_ctxt->num_expected++;
2300Sstevel@tonic-gate 			break;
2310Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
2320Sstevel@tonic-gate 			debug2("PAM echo off prompt: %s",
2330Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2340Sstevel@tonic-gate 			conv_ctxt->num_expected++;
2350Sstevel@tonic-gate 			break;
2360Sstevel@tonic-gate 		case PAM_TEXT_INFO:
2370Sstevel@tonic-gate 			debug2("PAM text info prompt: %s",
2380Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2390Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2400Sstevel@tonic-gate 			break;
2410Sstevel@tonic-gate 		case PAM_ERROR_MSG:
2420Sstevel@tonic-gate 			debug2("PAM error prompt: %s",
2430Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
2440Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2450Sstevel@tonic-gate 			break;
2460Sstevel@tonic-gate 		default:
2470Sstevel@tonic-gate 			/* Capture all these messages to be sent at once */
2480Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2490Sstevel@tonic-gate 			break;
2500Sstevel@tonic-gate 		}
2510Sstevel@tonic-gate 	}
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	if (conv_ctxt->num_expected == 0 && text == NULL) {
2540Sstevel@tonic-gate 		xfree(conv_ctxt);
2550Sstevel@tonic-gate 		return PAM_SUCCESS;
2560Sstevel@tonic-gate 	}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	authctxt->method->method_data = (void *) conv_ctxt;
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
2610Sstevel@tonic-gate 	packet_put_cstring("");	/* Name */
2620Sstevel@tonic-gate 	packet_put_cstring(text ? text : "");	/* Instructions */
2630Sstevel@tonic-gate 	packet_put_cstring("");	/* Language */
2640Sstevel@tonic-gate 	packet_put_int(conv_ctxt->num_expected);
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	if (text)
2670Sstevel@tonic-gate 		xfree(text);
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	for (i = 0, j = 0; i < num_msg; i++) {
2700Sstevel@tonic-gate 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 		/* Skip messages which don't need a reply */
2730Sstevel@tonic-gate 		if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF)
2740Sstevel@tonic-gate 			continue;
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 		conv_ctxt->prompts[j++] = i;
2770Sstevel@tonic-gate 		packet_put_cstring(PAM_MSG_MEMBER(msg, i, msg));
2780Sstevel@tonic-gate 		packet_put_char(style == PAM_PROMPT_ECHO_ON);
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate 	packet_send();
2810Sstevel@tonic-gate 	packet_write_wait();
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	/*
2840Sstevel@tonic-gate 	 * Here the dispatch_run() loop is nested.  It should be unwound
2850Sstevel@tonic-gate 	 * if keyboard-interactive userauth is abandoned (or restarted;
2860Sstevel@tonic-gate 	 * same thing).
2870Sstevel@tonic-gate 	 *
2880Sstevel@tonic-gate 	 * The condition for breaking out of the nested dispatch_run() loop is
2890Sstevel@tonic-gate 	 *     ((got kbd-int info reponse) || (kbd-int abandoned))
2900Sstevel@tonic-gate 	 *
2910Sstevel@tonic-gate 	 * conv_ctxt->finished is set in either of those cases.
2920Sstevel@tonic-gate 	 *
2930Sstevel@tonic-gate 	 * When abandonment is detected the conv_ctxt->finished is set as
2940Sstevel@tonic-gate 	 * is conv_ctxt->abandoned, causing this function to signal
2950Sstevel@tonic-gate 	 * userauth nested dispatch_run() loop unwinding and to return
2960Sstevel@tonic-gate 	 * PAM_CONV_ERR;
2970Sstevel@tonic-gate 	 */
2980Sstevel@tonic-gate 	debug2("Nesting dispatch_run loop");
2990Sstevel@tonic-gate 	dispatch_run(DISPATCH_BLOCK, &conv_ctxt->finished, appdata_ptr);
3000Sstevel@tonic-gate 	debug2("Nested dispatch_run loop exited");
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	if (conv_ctxt->abandoned) {
3030Sstevel@tonic-gate 		authctxt->unwind_dispatch_loop = 1;
3040Sstevel@tonic-gate 		xfree(conv_ctxt);
3050Sstevel@tonic-gate 		debug("PAM conv function returns PAM_CONV_ERR");
3060Sstevel@tonic-gate 		return PAM_CONV_ERR;
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	if (conv_ctxt->num_received == conv_ctxt->num_expected) {
3100Sstevel@tonic-gate 		*resp = conv_ctxt->responses;
3110Sstevel@tonic-gate 		xfree(conv_ctxt);
3120Sstevel@tonic-gate 		debug("PAM conv function returns PAM_SUCCESS");
3130Sstevel@tonic-gate 		return PAM_SUCCESS;
3140Sstevel@tonic-gate 	}
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 	debug("PAM conv function returns PAM_CONV_ERR");
3170Sstevel@tonic-gate 	xfree(conv_ctxt);
3180Sstevel@tonic-gate 	return PAM_CONV_ERR;
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate static void
3220Sstevel@tonic-gate input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt)
3230Sstevel@tonic-gate {
3240Sstevel@tonic-gate 	Authctxt *authctxt = ctxt;
3250Sstevel@tonic-gate 	Convctxt *conv_ctxt;
3260Sstevel@tonic-gate 	unsigned int nresp = 0, rlen = 0, i = 0;
3270Sstevel@tonic-gate 	char *resp;
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	if (authctxt == NULL)
3300Sstevel@tonic-gate 		fatal("input_userauth_info_response_pam: no authentication context");
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/* Check for spurious/unexpected info response */
3330Sstevel@tonic-gate 	if (method_kbdint.method_data == NULL) {
3340Sstevel@tonic-gate 		debug("input_userauth_info_response_pam: no method context");
3350Sstevel@tonic-gate 		return;
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	conv_ctxt = (Convctxt *) method_kbdint.method_data;
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	nresp = packet_get_int();	/* Number of responses. */
3410Sstevel@tonic-gate 	debug("got %d responses", nresp);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate #if 0
3450Sstevel@tonic-gate 	if (nresp != conv_ctxt->num_expected)
3460Sstevel@tonic-gate 		fatal("%s: Received incorrect number of responses "
3470Sstevel@tonic-gate 		    "(expected %d, received %u)", __func__,
3480Sstevel@tonic-gate 		    conv_ctxt->num_expected, nresp);
3490Sstevel@tonic-gate #endif
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	if (nresp > 100)
3520Sstevel@tonic-gate 		fatal("%s: too many replies", __func__);
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	for (i = 0; i < nresp && i < conv_ctxt->num_expected ; i++) {
3550Sstevel@tonic-gate 		int j = conv_ctxt->prompts[i];
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 		resp = packet_get_string(&rlen);
3580Sstevel@tonic-gate 		if (i < conv_ctxt->num_expected) {
3590Sstevel@tonic-gate 			conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS;
3600Sstevel@tonic-gate 			conv_ctxt->responses[j].resp = xstrdup(resp);
3610Sstevel@tonic-gate 			conv_ctxt->num_received++;
3620Sstevel@tonic-gate 		}
3630Sstevel@tonic-gate 		xfree(resp);
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	if (nresp < conv_ctxt->num_expected)
367*4775Svk199839 		fatal("%s: too few replies (%d < %d)", __func__,
368*4775Svk199839 		    nresp, conv_ctxt->num_expected);
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	/* XXX - This could make a covert channel... */
3710Sstevel@tonic-gate 	if (nresp > conv_ctxt->num_expected)
3720Sstevel@tonic-gate 		debug("Ignoring additional PAM replies");
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	conv_ctxt->finished = 1;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	packet_check_eom();
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate #if 0
3800Sstevel@tonic-gate int
3810Sstevel@tonic-gate kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method)
3820Sstevel@tonic-gate {
3830Sstevel@tonic-gate 	if (!method)
3840Sstevel@tonic-gate 		return 0; /* fatal(), really; it'll happen somewhere else */
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	if (!method->method_data)
3870Sstevel@tonic-gate 		return 0;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	return 1;
3900Sstevel@tonic-gate }
3910Sstevel@tonic-gate #endif
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate void
3940Sstevel@tonic-gate kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method)
3950Sstevel@tonic-gate {
3960Sstevel@tonic-gate 	Convctxt *conv_ctxt;
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	/*
3990Sstevel@tonic-gate 	 * But, if it ever becomes desirable and possible to support
4000Sstevel@tonic-gate 	 * kbd-int userauth abandonment, here's what must be done.
4010Sstevel@tonic-gate 	 */
4020Sstevel@tonic-gate 	if (!method)
4030Sstevel@tonic-gate 		return;
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	if (!method->method_data)
4060Sstevel@tonic-gate 		return;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	conv_ctxt = (Convctxt *) method->method_data;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	/* dispatch_run() loop will exit */
4110Sstevel@tonic-gate 	conv_ctxt->abandoned = 1;
4120Sstevel@tonic-gate 	conv_ctxt->finished = 1;
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	/*
4150Sstevel@tonic-gate 	 * The method_data will be free in the corresponding, active
4160Sstevel@tonic-gate 	 * conversation function
4170Sstevel@tonic-gate 	 */
4180Sstevel@tonic-gate 	method->method_data = NULL;
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 	/* update counts that can't be updated elsewhere */
4210Sstevel@tonic-gate 	method->abandons++;
4220Sstevel@tonic-gate 	method->attempts++;
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	/* Finally, we cannot re-use the current current PAM handle */
4250Sstevel@tonic-gate 	authctxt->pam->h = NULL;    /* Let the conv function cleanup */
4260Sstevel@tonic-gate }
4270Sstevel@tonic-gate #endif
428