xref: /onnv-gate/usr/src/cmd/ssh/sshd/auth2-pam.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
4*0Sstevel@tonic-gate  */
5*0Sstevel@tonic-gate 
6*0Sstevel@tonic-gate #include "includes.h"
7*0Sstevel@tonic-gate 
8*0Sstevel@tonic-gate RCSID("$Id: auth2-pam.c,v 1.14 2002/06/28 16:48:12 mouring Exp $");
9*0Sstevel@tonic-gate 
10*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
11*0Sstevel@tonic-gate 
12*0Sstevel@tonic-gate #ifdef USE_PAM
13*0Sstevel@tonic-gate #include <security/pam_appl.h>
14*0Sstevel@tonic-gate 
15*0Sstevel@tonic-gate #include "ssh.h"
16*0Sstevel@tonic-gate #include "ssh2.h"
17*0Sstevel@tonic-gate #include "auth.h"
18*0Sstevel@tonic-gate #include "auth-pam.h"
19*0Sstevel@tonic-gate #include "auth-options.h"
20*0Sstevel@tonic-gate #include "packet.h"
21*0Sstevel@tonic-gate #include "xmalloc.h"
22*0Sstevel@tonic-gate #include "dispatch.h"
23*0Sstevel@tonic-gate #include "canohost.h"
24*0Sstevel@tonic-gate #include "log.h"
25*0Sstevel@tonic-gate #include "servconf.h"
26*0Sstevel@tonic-gate #include "monitor_wrap.h"
27*0Sstevel@tonic-gate #include "misc.h"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #ifdef HAVE_BSM
30*0Sstevel@tonic-gate #include "bsmaudit.h"
31*0Sstevel@tonic-gate #endif /* HAVE_BSM */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate extern u_int utmp_len;
34*0Sstevel@tonic-gate extern ServerOptions options;
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate extern Authmethod method_kbdint;
37*0Sstevel@tonic-gate extern Authmethod method_passwd;
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate #define SSHD_PAM_KBDINT_SVC "sshd-kbdint"
40*0Sstevel@tonic-gate 
41*0Sstevel@tonic-gate static int do_pam_conv_kbd_int(int num_msg,
42*0Sstevel@tonic-gate     struct pam_message **msg, struct pam_response **resp,
43*0Sstevel@tonic-gate     void *appdata_ptr);
44*0Sstevel@tonic-gate static void input_userauth_info_response_pam(int type,
45*0Sstevel@tonic-gate 					     u_int32_t seqnr,
46*0Sstevel@tonic-gate 					     void *ctxt);
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate static struct pam_conv conv2 = {
49*0Sstevel@tonic-gate 	do_pam_conv_kbd_int,
50*0Sstevel@tonic-gate 	NULL,
51*0Sstevel@tonic-gate };
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate static void do_pam_kbdint_cleanup(pam_handle_t *pamh);
54*0Sstevel@tonic-gate static void do_pam_kbdint(Authctxt *authctxt);
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate void
57*0Sstevel@tonic-gate auth2_pam(Authctxt *authctxt)
58*0Sstevel@tonic-gate {
59*0Sstevel@tonic-gate 	if (authctxt->user == NULL)
60*0Sstevel@tonic-gate 		fatal("auth2_pam: internal error: no user");
61*0Sstevel@tonic-gate 	if (authctxt->method == NULL)
62*0Sstevel@tonic-gate 		fatal("auth2_pam: internal error: no method");
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate 	conv2.appdata_ptr = authctxt;
65*0Sstevel@tonic-gate 	new_start_pam(authctxt, &conv2);
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate 	authctxt->method->method_data = NULL; /* freed in the conv func */
68*0Sstevel@tonic-gate 	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
69*0Sstevel@tonic-gate 	    &input_userauth_info_response_pam);
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate 	/*
72*0Sstevel@tonic-gate 	 * Since password userauth and keyboard-interactive userauth
73*0Sstevel@tonic-gate 	 * both use PAM, and since keyboard-interactive is so much
74*0Sstevel@tonic-gate 	 * better than password userauth, we should not allow the user
75*0Sstevel@tonic-gate 	 * to try password userauth after trying keyboard-interactive.
76*0Sstevel@tonic-gate 	 */
77*0Sstevel@tonic-gate 	if (method_passwd.enabled)
78*0Sstevel@tonic-gate 		*method_passwd.enabled = 0;
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate 	do_pam_kbdint(authctxt);
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate 	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
83*0Sstevel@tonic-gate }
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate static void
86*0Sstevel@tonic-gate do_pam_kbdint(Authctxt *authctxt)
87*0Sstevel@tonic-gate {
88*0Sstevel@tonic-gate 	int		 retval, retval2;
89*0Sstevel@tonic-gate 	pam_handle_t	*pamh = authctxt->pam->h;
90*0Sstevel@tonic-gate 	const char	*where = "authenticating";
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate 	debug2("Calling pam_authenticate()");
93*0Sstevel@tonic-gate 	if ((retval = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
94*0Sstevel@tonic-gate 		goto cleanup;
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 	debug2("kbd-int: pam_authenticate() succeeded");
97*0Sstevel@tonic-gate 	where = "authorizing";
98*0Sstevel@tonic-gate 	retval = pam_acct_mgmt(pamh, 0);
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate 	if (retval == PAM_NEW_AUTHTOK_REQD) {
101*0Sstevel@tonic-gate 		if (authctxt->valid && authctxt->pw != NULL) {
102*0Sstevel@tonic-gate 			/*
103*0Sstevel@tonic-gate 			 * Can't use temporarily_use_uid() and restore_uid()
104*0Sstevel@tonic-gate 			 * here because we need (euid == 0 && ruid == pw_uid)
105*0Sstevel@tonic-gate 			 * whereas temporarily_use_uid() arranges for
106*0Sstevel@tonic-gate 			 * (suid = 0 && euid == pw_uid && ruid == pw_uid).
107*0Sstevel@tonic-gate 			 */
108*0Sstevel@tonic-gate 			(void) setreuid(authctxt->pw->pw_uid, -1);
109*0Sstevel@tonic-gate 			debug2("kbd-int: changing expired password");
110*0Sstevel@tonic-gate 			where = "changing authentication tokens (password)";
111*0Sstevel@tonic-gate 			retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
112*0Sstevel@tonic-gate 			audit_sshd_chauthtok(retval, authctxt->pw->pw_uid,
113*0Sstevel@tonic-gate 				authctxt->pw->pw_gid);
114*0Sstevel@tonic-gate 			(void) setreuid(0, -1);
115*0Sstevel@tonic-gate 		} else {
116*0Sstevel@tonic-gate 			retval = PAM_PERM_DENIED;
117*0Sstevel@tonic-gate 		}
118*0Sstevel@tonic-gate 	}
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	if (retval != PAM_SUCCESS)
121*0Sstevel@tonic-gate 		goto cleanup;
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate 	authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 	retval = finish_userauth_do_pam(authctxt);
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate 	if (retval != PAM_SUCCESS)
128*0Sstevel@tonic-gate 		goto cleanup;
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate 	/*
131*0Sstevel@tonic-gate 	 * PAM handle stays around so we can call pam_close_session()
132*0Sstevel@tonic-gate 	 * on it later.
133*0Sstevel@tonic-gate 	 */
134*0Sstevel@tonic-gate 	authctxt->method->authenticated = 1;
135*0Sstevel@tonic-gate 	debug2("kbd-int: success (pam->state == %x)", authctxt->pam->state);
136*0Sstevel@tonic-gate 	return;
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate cleanup:
139*0Sstevel@tonic-gate 	/*
140*0Sstevel@tonic-gate 	 * Check for abandonment and cleanup.  When kbdint is abandoned
141*0Sstevel@tonic-gate 	 * authctxt->pam->h is NULLed and by this point a new handle may
142*0Sstevel@tonic-gate 	 * be allocated.
143*0Sstevel@tonic-gate 	 */
144*0Sstevel@tonic-gate 	if (authctxt->pam->h != pamh) {
145*0Sstevel@tonic-gate 		log("Keyboard-interactive (PAM) userauth abandoned "
146*0Sstevel@tonic-gate 		    "while %s", where);
147*0Sstevel@tonic-gate 		if ((retval2 = pam_end(pamh, retval)) != PAM_SUCCESS) {
148*0Sstevel@tonic-gate 			log("Cannot close PAM handle after "
149*0Sstevel@tonic-gate 			    "kbd-int userauth abandonment[%d]: %.200s",
150*0Sstevel@tonic-gate 			    retval2, PAM_STRERROR(pamh, retval2));
151*0Sstevel@tonic-gate 		}
152*0Sstevel@tonic-gate 		authctxt->method->abandoned = 1;
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 		/*
155*0Sstevel@tonic-gate 		 * Avoid double counting; these are incremented in
156*0Sstevel@tonic-gate 		 * kbdint_pam_abandon() so that they reflect the correct
157*0Sstevel@tonic-gate 		 * count when userauth_finish() is called before
158*0Sstevel@tonic-gate 		 * unwinding the dispatch_run() loop, but they are
159*0Sstevel@tonic-gate 		 * incremented again in input_userauth_request() when
160*0Sstevel@tonic-gate 		 * the loop is unwound, right here.
161*0Sstevel@tonic-gate 		 */
162*0Sstevel@tonic-gate 		if (authctxt->method->abandons)
163*0Sstevel@tonic-gate 			authctxt->method->abandons--;
164*0Sstevel@tonic-gate 		if (authctxt->method->attempts)
165*0Sstevel@tonic-gate 			authctxt->method->attempts--;
166*0Sstevel@tonic-gate 	}
167*0Sstevel@tonic-gate 	else {
168*0Sstevel@tonic-gate 		/* Save error value for pam_end() */
169*0Sstevel@tonic-gate 		authctxt->pam->last_pam_retval = retval;
170*0Sstevel@tonic-gate 		log("Keyboard-interactive (PAM) userauth failed[%d] "
171*0Sstevel@tonic-gate 		    "while %s: %.200s", retval, where,
172*0Sstevel@tonic-gate 		    PAM_STRERROR(pamh, retval));
173*0Sstevel@tonic-gate 		/* pam handle can be reused elsewhere, so no pam_end() here */
174*0Sstevel@tonic-gate 	}
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate 	return;
177*0Sstevel@tonic-gate }
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate static int
180*0Sstevel@tonic-gate do_pam_conv_kbd_int(int num_msg, struct pam_message **msg,
181*0Sstevel@tonic-gate     struct pam_response **resp, void *appdata_ptr)
182*0Sstevel@tonic-gate {
183*0Sstevel@tonic-gate 	int i, j;
184*0Sstevel@tonic-gate 	char *text;
185*0Sstevel@tonic-gate 	Convctxt *conv_ctxt;
186*0Sstevel@tonic-gate 	Authctxt *authctxt = (Authctxt *)appdata_ptr;
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	if (!authctxt || !authctxt->method) {
189*0Sstevel@tonic-gate 		debug("Missing state during PAM conversation");
190*0Sstevel@tonic-gate 		return PAM_CONV_ERR;
191*0Sstevel@tonic-gate 	}
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	conv_ctxt = xmalloc(sizeof(Convctxt));
194*0Sstevel@tonic-gate 	(void) memset(conv_ctxt, 0, sizeof(Convctxt));
195*0Sstevel@tonic-gate 	conv_ctxt->finished = 0;
196*0Sstevel@tonic-gate 	conv_ctxt->num_received = 0;
197*0Sstevel@tonic-gate 	conv_ctxt->num_expected = 0;
198*0Sstevel@tonic-gate 	conv_ctxt->prompts = xmalloc(sizeof(int) * num_msg);
199*0Sstevel@tonic-gate 	conv_ctxt->responses = xmalloc(sizeof(struct pam_response) * num_msg);
200*0Sstevel@tonic-gate 	(void) memset(conv_ctxt->responses, 0, sizeof(struct pam_response) * num_msg);
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	text = NULL;
203*0Sstevel@tonic-gate 	for (i = 0, conv_ctxt->num_expected = 0; i < num_msg; i++) {
204*0Sstevel@tonic-gate 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
205*0Sstevel@tonic-gate 		switch (style) {
206*0Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_ON:
207*0Sstevel@tonic-gate 			debug2("PAM echo on prompt: %s",
208*0Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
209*0Sstevel@tonic-gate 			conv_ctxt->num_expected++;
210*0Sstevel@tonic-gate 			break;
211*0Sstevel@tonic-gate 		case PAM_PROMPT_ECHO_OFF:
212*0Sstevel@tonic-gate 			debug2("PAM echo off prompt: %s",
213*0Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
214*0Sstevel@tonic-gate 			conv_ctxt->num_expected++;
215*0Sstevel@tonic-gate 			break;
216*0Sstevel@tonic-gate 		case PAM_TEXT_INFO:
217*0Sstevel@tonic-gate 			debug2("PAM text info prompt: %s",
218*0Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
219*0Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
220*0Sstevel@tonic-gate 			break;
221*0Sstevel@tonic-gate 		case PAM_ERROR_MSG:
222*0Sstevel@tonic-gate 			debug2("PAM error prompt: %s",
223*0Sstevel@tonic-gate 				PAM_MSG_MEMBER(msg, i, msg));
224*0Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
225*0Sstevel@tonic-gate 			break;
226*0Sstevel@tonic-gate 		default:
227*0Sstevel@tonic-gate 			/* Capture all these messages to be sent at once */
228*0Sstevel@tonic-gate 			message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
229*0Sstevel@tonic-gate 			break;
230*0Sstevel@tonic-gate 		}
231*0Sstevel@tonic-gate 	}
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	if (conv_ctxt->num_expected == 0 && text == NULL) {
234*0Sstevel@tonic-gate 		xfree(conv_ctxt);
235*0Sstevel@tonic-gate 		return PAM_SUCCESS;
236*0Sstevel@tonic-gate 	}
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 	authctxt->method->method_data = (void *) conv_ctxt;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
241*0Sstevel@tonic-gate 	packet_put_cstring("");	/* Name */
242*0Sstevel@tonic-gate 	packet_put_cstring(text ? text : "");	/* Instructions */
243*0Sstevel@tonic-gate 	packet_put_cstring("");	/* Language */
244*0Sstevel@tonic-gate 	packet_put_int(conv_ctxt->num_expected);
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	if (text)
247*0Sstevel@tonic-gate 		xfree(text);
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	for (i = 0, j = 0; i < num_msg; i++) {
250*0Sstevel@tonic-gate 		int style = PAM_MSG_MEMBER(msg, i, msg_style);
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 		/* Skip messages which don't need a reply */
253*0Sstevel@tonic-gate 		if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF)
254*0Sstevel@tonic-gate 			continue;
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 		conv_ctxt->prompts[j++] = i;
257*0Sstevel@tonic-gate 		packet_put_cstring(PAM_MSG_MEMBER(msg, i, msg));
258*0Sstevel@tonic-gate 		packet_put_char(style == PAM_PROMPT_ECHO_ON);
259*0Sstevel@tonic-gate 	}
260*0Sstevel@tonic-gate 	packet_send();
261*0Sstevel@tonic-gate 	packet_write_wait();
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	/*
264*0Sstevel@tonic-gate 	 * Here the dispatch_run() loop is nested.  It should be unwound
265*0Sstevel@tonic-gate 	 * if keyboard-interactive userauth is abandoned (or restarted;
266*0Sstevel@tonic-gate 	 * same thing).
267*0Sstevel@tonic-gate 	 *
268*0Sstevel@tonic-gate 	 * The condition for breaking out of the nested dispatch_run() loop is
269*0Sstevel@tonic-gate 	 *     ((got kbd-int info reponse) || (kbd-int abandoned))
270*0Sstevel@tonic-gate 	 *
271*0Sstevel@tonic-gate 	 * conv_ctxt->finished is set in either of those cases.
272*0Sstevel@tonic-gate 	 *
273*0Sstevel@tonic-gate 	 * When abandonment is detected the conv_ctxt->finished is set as
274*0Sstevel@tonic-gate 	 * is conv_ctxt->abandoned, causing this function to signal
275*0Sstevel@tonic-gate 	 * userauth nested dispatch_run() loop unwinding and to return
276*0Sstevel@tonic-gate 	 * PAM_CONV_ERR;
277*0Sstevel@tonic-gate 	 */
278*0Sstevel@tonic-gate 	debug2("Nesting dispatch_run loop");
279*0Sstevel@tonic-gate 	dispatch_run(DISPATCH_BLOCK, &conv_ctxt->finished, appdata_ptr);
280*0Sstevel@tonic-gate 	debug2("Nested dispatch_run loop exited");
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	if (conv_ctxt->abandoned) {
283*0Sstevel@tonic-gate 		authctxt->unwind_dispatch_loop = 1;
284*0Sstevel@tonic-gate 		xfree(conv_ctxt);
285*0Sstevel@tonic-gate 		debug("PAM conv function returns PAM_CONV_ERR");
286*0Sstevel@tonic-gate 		return PAM_CONV_ERR;
287*0Sstevel@tonic-gate 	}
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	if (conv_ctxt->num_received == conv_ctxt->num_expected) {
290*0Sstevel@tonic-gate 		*resp = conv_ctxt->responses;
291*0Sstevel@tonic-gate 		xfree(conv_ctxt);
292*0Sstevel@tonic-gate 		debug("PAM conv function returns PAM_SUCCESS");
293*0Sstevel@tonic-gate 		return PAM_SUCCESS;
294*0Sstevel@tonic-gate 	}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	debug("PAM conv function returns PAM_CONV_ERR");
297*0Sstevel@tonic-gate 	xfree(conv_ctxt);
298*0Sstevel@tonic-gate 	return PAM_CONV_ERR;
299*0Sstevel@tonic-gate }
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate static void
302*0Sstevel@tonic-gate input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt)
303*0Sstevel@tonic-gate {
304*0Sstevel@tonic-gate 	Authctxt *authctxt = ctxt;
305*0Sstevel@tonic-gate 	Convctxt *conv_ctxt;
306*0Sstevel@tonic-gate 	unsigned int nresp = 0, rlen = 0, i = 0;
307*0Sstevel@tonic-gate 	char *resp;
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	if (authctxt == NULL)
310*0Sstevel@tonic-gate 		fatal("input_userauth_info_response_pam: no authentication context");
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	/* Check for spurious/unexpected info response */
313*0Sstevel@tonic-gate 	if (method_kbdint.method_data == NULL) {
314*0Sstevel@tonic-gate 		debug("input_userauth_info_response_pam: no method context");
315*0Sstevel@tonic-gate 		return;
316*0Sstevel@tonic-gate 	}
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	conv_ctxt = (Convctxt *) method_kbdint.method_data;
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	nresp = packet_get_int();	/* Number of responses. */
321*0Sstevel@tonic-gate 	debug("got %d responses", nresp);
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate #if 0
325*0Sstevel@tonic-gate 	if (nresp != conv_ctxt->num_expected)
326*0Sstevel@tonic-gate 		fatal("%s: Received incorrect number of responses "
327*0Sstevel@tonic-gate 		    "(expected %d, received %u)", __func__,
328*0Sstevel@tonic-gate 		    conv_ctxt->num_expected, nresp);
329*0Sstevel@tonic-gate #endif
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 	if (nresp > 100)
332*0Sstevel@tonic-gate 		fatal("%s: too many replies", __func__);
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate 	for (i = 0; i < nresp && i < conv_ctxt->num_expected ; i++) {
335*0Sstevel@tonic-gate 		int j = conv_ctxt->prompts[i];
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 		resp = packet_get_string(&rlen);
338*0Sstevel@tonic-gate 		if (i < conv_ctxt->num_expected) {
339*0Sstevel@tonic-gate 			conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS;
340*0Sstevel@tonic-gate 			conv_ctxt->responses[j].resp = xstrdup(resp);
341*0Sstevel@tonic-gate 			conv_ctxt->num_received++;
342*0Sstevel@tonic-gate 		}
343*0Sstevel@tonic-gate 		xfree(resp);
344*0Sstevel@tonic-gate 	}
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	if (nresp < conv_ctxt->num_expected)
347*0Sstevel@tonic-gate 		fatal("%s: too few replies", __func__);
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate 	/* XXX - This could make a covert channel... */
350*0Sstevel@tonic-gate 	if (nresp > conv_ctxt->num_expected)
351*0Sstevel@tonic-gate 		debug("Ignoring additional PAM replies");
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	conv_ctxt->finished = 1;
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	packet_check_eom();
356*0Sstevel@tonic-gate }
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate #if 0
359*0Sstevel@tonic-gate int
360*0Sstevel@tonic-gate kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method)
361*0Sstevel@tonic-gate {
362*0Sstevel@tonic-gate 	if (!method)
363*0Sstevel@tonic-gate 		return 0; /* fatal(), really; it'll happen somewhere else */
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 	if (!method->method_data)
366*0Sstevel@tonic-gate 		return 0;
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 	return 1;
369*0Sstevel@tonic-gate }
370*0Sstevel@tonic-gate #endif
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate void
373*0Sstevel@tonic-gate kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method)
374*0Sstevel@tonic-gate {
375*0Sstevel@tonic-gate 	Convctxt *conv_ctxt;
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 	/*
378*0Sstevel@tonic-gate 	 * But, if it ever becomes desirable and possible to support
379*0Sstevel@tonic-gate 	 * kbd-int userauth abandonment, here's what must be done.
380*0Sstevel@tonic-gate 	 */
381*0Sstevel@tonic-gate 	if (!method)
382*0Sstevel@tonic-gate 		return;
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 	if (!method->method_data)
385*0Sstevel@tonic-gate 		return;
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate 	conv_ctxt = (Convctxt *) method->method_data;
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	/* dispatch_run() loop will exit */
390*0Sstevel@tonic-gate 	conv_ctxt->abandoned = 1;
391*0Sstevel@tonic-gate 	conv_ctxt->finished = 1;
392*0Sstevel@tonic-gate 
393*0Sstevel@tonic-gate 	/*
394*0Sstevel@tonic-gate 	 * The method_data will be free in the corresponding, active
395*0Sstevel@tonic-gate 	 * conversation function
396*0Sstevel@tonic-gate 	 */
397*0Sstevel@tonic-gate 	method->method_data = NULL;
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	/* update counts that can't be updated elsewhere */
400*0Sstevel@tonic-gate 	method->abandons++;
401*0Sstevel@tonic-gate 	method->attempts++;
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	/* Finally, we cannot re-use the current current PAM handle */
404*0Sstevel@tonic-gate 	authctxt->pam->h = NULL;    /* Let the conv function cleanup */
405*0Sstevel@tonic-gate }
406*0Sstevel@tonic-gate #endif
407