10Sstevel@tonic-gate /*
2*9600SNobutomo.Nakano@Sun.COM * Copyright 2009 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 #ifdef USE_PAM
110Sstevel@tonic-gate #include <security/pam_appl.h>
120Sstevel@tonic-gate
130Sstevel@tonic-gate #include "ssh.h"
140Sstevel@tonic-gate #include "ssh2.h"
150Sstevel@tonic-gate #include "auth.h"
160Sstevel@tonic-gate #include "auth-pam.h"
170Sstevel@tonic-gate #include "auth-options.h"
180Sstevel@tonic-gate #include "packet.h"
190Sstevel@tonic-gate #include "xmalloc.h"
200Sstevel@tonic-gate #include "dispatch.h"
210Sstevel@tonic-gate #include "canohost.h"
220Sstevel@tonic-gate #include "log.h"
230Sstevel@tonic-gate #include "servconf.h"
240Sstevel@tonic-gate #include "misc.h"
250Sstevel@tonic-gate
260Sstevel@tonic-gate #ifdef HAVE_BSM
270Sstevel@tonic-gate #include "bsmaudit.h"
280Sstevel@tonic-gate #endif /* HAVE_BSM */
290Sstevel@tonic-gate
300Sstevel@tonic-gate extern u_int utmp_len;
310Sstevel@tonic-gate extern ServerOptions options;
320Sstevel@tonic-gate
330Sstevel@tonic-gate extern Authmethod method_kbdint;
340Sstevel@tonic-gate extern Authmethod method_passwd;
350Sstevel@tonic-gate
360Sstevel@tonic-gate #define SSHD_PAM_KBDINT_SVC "sshd-kbdint"
378305SPeter.Shoults@Sun.COM /* Maximum attempts for changing expired password */
388305SPeter.Shoults@Sun.COM #define DEF_ATTEMPTS 3
390Sstevel@tonic-gate
400Sstevel@tonic-gate static int do_pam_conv_kbd_int(int num_msg,
410Sstevel@tonic-gate struct pam_message **msg, struct pam_response **resp,
420Sstevel@tonic-gate void *appdata_ptr);
430Sstevel@tonic-gate static void input_userauth_info_response_pam(int type,
440Sstevel@tonic-gate u_int32_t seqnr,
450Sstevel@tonic-gate void *ctxt);
460Sstevel@tonic-gate
470Sstevel@tonic-gate static struct pam_conv conv2 = {
480Sstevel@tonic-gate do_pam_conv_kbd_int,
490Sstevel@tonic-gate NULL,
500Sstevel@tonic-gate };
510Sstevel@tonic-gate
520Sstevel@tonic-gate static void do_pam_kbdint_cleanup(pam_handle_t *pamh);
530Sstevel@tonic-gate static void do_pam_kbdint(Authctxt *authctxt);
540Sstevel@tonic-gate
550Sstevel@tonic-gate void
auth2_pam(Authctxt * authctxt)560Sstevel@tonic-gate auth2_pam(Authctxt *authctxt)
570Sstevel@tonic-gate {
580Sstevel@tonic-gate if (authctxt->user == NULL)
590Sstevel@tonic-gate fatal("auth2_pam: internal error: no user");
600Sstevel@tonic-gate if (authctxt->method == NULL)
610Sstevel@tonic-gate fatal("auth2_pam: internal error: no method");
620Sstevel@tonic-gate
630Sstevel@tonic-gate conv2.appdata_ptr = authctxt;
640Sstevel@tonic-gate new_start_pam(authctxt, &conv2);
650Sstevel@tonic-gate
660Sstevel@tonic-gate authctxt->method->method_data = NULL; /* freed in the conv func */
670Sstevel@tonic-gate dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
680Sstevel@tonic-gate &input_userauth_info_response_pam);
690Sstevel@tonic-gate
700Sstevel@tonic-gate /*
710Sstevel@tonic-gate * Since password userauth and keyboard-interactive userauth
720Sstevel@tonic-gate * both use PAM, and since keyboard-interactive is so much
730Sstevel@tonic-gate * better than password userauth, we should not allow the user
740Sstevel@tonic-gate * to try password userauth after trying keyboard-interactive.
750Sstevel@tonic-gate */
760Sstevel@tonic-gate if (method_passwd.enabled)
770Sstevel@tonic-gate *method_passwd.enabled = 0;
780Sstevel@tonic-gate
790Sstevel@tonic-gate do_pam_kbdint(authctxt);
800Sstevel@tonic-gate
810Sstevel@tonic-gate dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
820Sstevel@tonic-gate }
830Sstevel@tonic-gate
840Sstevel@tonic-gate static void
do_pam_kbdint(Authctxt * authctxt)850Sstevel@tonic-gate do_pam_kbdint(Authctxt *authctxt)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate int retval, retval2;
880Sstevel@tonic-gate pam_handle_t *pamh = authctxt->pam->h;
890Sstevel@tonic-gate const char *where = "authenticating";
904775Svk199839 char *text = NULL;
910Sstevel@tonic-gate
920Sstevel@tonic-gate debug2("Calling pam_authenticate()");
937095Sdarrenm retval = pam_authenticate(pamh,
947095Sdarrenm options.permit_empty_passwd ? 0 :
957095Sdarrenm PAM_DISALLOW_NULL_AUTHTOK);
967095Sdarrenm
977095Sdarrenm if (retval != PAM_SUCCESS)
980Sstevel@tonic-gate goto cleanup;
990Sstevel@tonic-gate
1000Sstevel@tonic-gate debug2("kbd-int: pam_authenticate() succeeded");
1010Sstevel@tonic-gate where = "authorizing";
1020Sstevel@tonic-gate retval = pam_acct_mgmt(pamh, 0);
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate if (retval == PAM_NEW_AUTHTOK_REQD) {
1050Sstevel@tonic-gate if (authctxt->valid && authctxt->pw != NULL) {
1064775Svk199839 /* send password expiration warning */
1074775Svk199839 message_cat(&text,
1084775Svk199839 gettext("Warning: Your password has expired,"
1094775Svk199839 " please change it now."));
1104775Svk199839 packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
1114775Svk199839 packet_put_cstring(""); /* name */
112*9600SNobutomo.Nakano@Sun.COM packet_put_utf8_cstring(text); /* instructions */
1134775Svk199839 packet_put_cstring(""); /* language, unused */
1144775Svk199839 packet_put_int(0);
1154775Svk199839 packet_send();
1164775Svk199839 packet_write_wait();
1174775Svk199839 debug("expiration message sent");
1184775Svk199839 if (text)
1194775Svk199839 xfree(text);
1204775Svk199839 /*
1214775Svk199839 * wait for the response so it does not mix
1224775Svk199839 * with the upcoming PAM conversation
1234775Svk199839 */
1244775Svk199839 packet_read_expect(SSH2_MSG_USERAUTH_INFO_RESPONSE);
1250Sstevel@tonic-gate /*
1260Sstevel@tonic-gate * Can't use temporarily_use_uid() and restore_uid()
1270Sstevel@tonic-gate * here because we need (euid == 0 && ruid == pw_uid)
1280Sstevel@tonic-gate * whereas temporarily_use_uid() arranges for
1290Sstevel@tonic-gate * (suid = 0 && euid == pw_uid && ruid == pw_uid).
1300Sstevel@tonic-gate */
1310Sstevel@tonic-gate (void) setreuid(authctxt->pw->pw_uid, -1);
1320Sstevel@tonic-gate debug2("kbd-int: changing expired password");
1330Sstevel@tonic-gate where = "changing authentication tokens (password)";
1348305SPeter.Shoults@Sun.COM /*
1358305SPeter.Shoults@Sun.COM * Depending on error returned from pam_chauthtok, we
1368305SPeter.Shoults@Sun.COM * need to try to change password a few times before
1378305SPeter.Shoults@Sun.COM * we error out and return.
1388305SPeter.Shoults@Sun.COM */
1398305SPeter.Shoults@Sun.COM int tries = 0;
1408305SPeter.Shoults@Sun.COM while ((retval = pam_chauthtok(pamh,
1418305SPeter.Shoults@Sun.COM PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) {
1428305SPeter.Shoults@Sun.COM if (tries++ < DEF_ATTEMPTS) {
1438305SPeter.Shoults@Sun.COM if ((retval == PAM_AUTHTOK_ERR) ||
1448305SPeter.Shoults@Sun.COM (retval == PAM_TRY_AGAIN)) {
1458305SPeter.Shoults@Sun.COM continue;
1468305SPeter.Shoults@Sun.COM }
1478305SPeter.Shoults@Sun.COM }
1488305SPeter.Shoults@Sun.COM break;
1498305SPeter.Shoults@Sun.COM }
1500Sstevel@tonic-gate audit_sshd_chauthtok(retval, authctxt->pw->pw_uid,
1510Sstevel@tonic-gate authctxt->pw->pw_gid);
1520Sstevel@tonic-gate (void) setreuid(0, -1);
1530Sstevel@tonic-gate } else {
1540Sstevel@tonic-gate retval = PAM_PERM_DENIED;
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate if (retval != PAM_SUCCESS)
1590Sstevel@tonic-gate goto cleanup;
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate retval = finish_userauth_do_pam(authctxt);
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate if (retval != PAM_SUCCESS)
1660Sstevel@tonic-gate goto cleanup;
1670Sstevel@tonic-gate
1680Sstevel@tonic-gate /*
1690Sstevel@tonic-gate * PAM handle stays around so we can call pam_close_session()
1700Sstevel@tonic-gate * on it later.
1710Sstevel@tonic-gate */
1720Sstevel@tonic-gate authctxt->method->authenticated = 1;
1730Sstevel@tonic-gate debug2("kbd-int: success (pam->state == %x)", authctxt->pam->state);
1740Sstevel@tonic-gate return;
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate cleanup:
1770Sstevel@tonic-gate /*
1780Sstevel@tonic-gate * Check for abandonment and cleanup. When kbdint is abandoned
1790Sstevel@tonic-gate * authctxt->pam->h is NULLed and by this point a new handle may
1800Sstevel@tonic-gate * be allocated.
1810Sstevel@tonic-gate */
1820Sstevel@tonic-gate if (authctxt->pam->h != pamh) {
1830Sstevel@tonic-gate log("Keyboard-interactive (PAM) userauth abandoned "
1840Sstevel@tonic-gate "while %s", where);
1850Sstevel@tonic-gate if ((retval2 = pam_end(pamh, retval)) != PAM_SUCCESS) {
1860Sstevel@tonic-gate log("Cannot close PAM handle after "
1870Sstevel@tonic-gate "kbd-int userauth abandonment[%d]: %.200s",
1880Sstevel@tonic-gate retval2, PAM_STRERROR(pamh, retval2));
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate authctxt->method->abandoned = 1;
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate * Avoid double counting; these are incremented in
1940Sstevel@tonic-gate * kbdint_pam_abandon() so that they reflect the correct
1950Sstevel@tonic-gate * count when userauth_finish() is called before
1960Sstevel@tonic-gate * unwinding the dispatch_run() loop, but they are
1970Sstevel@tonic-gate * incremented again in input_userauth_request() when
1980Sstevel@tonic-gate * the loop is unwound, right here.
1990Sstevel@tonic-gate */
2000Sstevel@tonic-gate if (authctxt->method->abandons)
2010Sstevel@tonic-gate authctxt->method->abandons--;
2020Sstevel@tonic-gate if (authctxt->method->attempts)
2030Sstevel@tonic-gate authctxt->method->attempts--;
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate else {
2060Sstevel@tonic-gate /* Save error value for pam_end() */
2070Sstevel@tonic-gate authctxt->pam->last_pam_retval = retval;
2080Sstevel@tonic-gate log("Keyboard-interactive (PAM) userauth failed[%d] "
2090Sstevel@tonic-gate "while %s: %.200s", retval, where,
2100Sstevel@tonic-gate PAM_STRERROR(pamh, retval));
2110Sstevel@tonic-gate /* pam handle can be reused elsewhere, so no pam_end() here */
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate return;
2150Sstevel@tonic-gate }
2160Sstevel@tonic-gate
2170Sstevel@tonic-gate static int
do_pam_conv_kbd_int(int num_msg,struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)2180Sstevel@tonic-gate do_pam_conv_kbd_int(int num_msg, struct pam_message **msg,
2190Sstevel@tonic-gate struct pam_response **resp, void *appdata_ptr)
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate int i, j;
2220Sstevel@tonic-gate char *text;
2230Sstevel@tonic-gate Convctxt *conv_ctxt;
2240Sstevel@tonic-gate Authctxt *authctxt = (Authctxt *)appdata_ptr;
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate if (!authctxt || !authctxt->method) {
2270Sstevel@tonic-gate debug("Missing state during PAM conversation");
2280Sstevel@tonic-gate return PAM_CONV_ERR;
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate conv_ctxt = xmalloc(sizeof(Convctxt));
2320Sstevel@tonic-gate (void) memset(conv_ctxt, 0, sizeof(Convctxt));
2330Sstevel@tonic-gate conv_ctxt->finished = 0;
2340Sstevel@tonic-gate conv_ctxt->num_received = 0;
2350Sstevel@tonic-gate conv_ctxt->num_expected = 0;
2360Sstevel@tonic-gate conv_ctxt->prompts = xmalloc(sizeof(int) * num_msg);
2370Sstevel@tonic-gate conv_ctxt->responses = xmalloc(sizeof(struct pam_response) * num_msg);
2380Sstevel@tonic-gate (void) memset(conv_ctxt->responses, 0, sizeof(struct pam_response) * num_msg);
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate text = NULL;
2410Sstevel@tonic-gate for (i = 0, conv_ctxt->num_expected = 0; i < num_msg; i++) {
2420Sstevel@tonic-gate int style = PAM_MSG_MEMBER(msg, i, msg_style);
2430Sstevel@tonic-gate switch (style) {
2440Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON:
2450Sstevel@tonic-gate debug2("PAM echo on prompt: %s",
2460Sstevel@tonic-gate PAM_MSG_MEMBER(msg, i, msg));
2470Sstevel@tonic-gate conv_ctxt->num_expected++;
2480Sstevel@tonic-gate break;
2490Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF:
2500Sstevel@tonic-gate debug2("PAM echo off prompt: %s",
2510Sstevel@tonic-gate PAM_MSG_MEMBER(msg, i, msg));
2520Sstevel@tonic-gate conv_ctxt->num_expected++;
2530Sstevel@tonic-gate break;
2540Sstevel@tonic-gate case PAM_TEXT_INFO:
2550Sstevel@tonic-gate debug2("PAM text info prompt: %s",
2560Sstevel@tonic-gate PAM_MSG_MEMBER(msg, i, msg));
2570Sstevel@tonic-gate message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2580Sstevel@tonic-gate break;
2590Sstevel@tonic-gate case PAM_ERROR_MSG:
2600Sstevel@tonic-gate debug2("PAM error prompt: %s",
2610Sstevel@tonic-gate PAM_MSG_MEMBER(msg, i, msg));
2620Sstevel@tonic-gate message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2630Sstevel@tonic-gate break;
2640Sstevel@tonic-gate default:
2650Sstevel@tonic-gate /* Capture all these messages to be sent at once */
2660Sstevel@tonic-gate message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
2670Sstevel@tonic-gate break;
2680Sstevel@tonic-gate }
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate if (conv_ctxt->num_expected == 0 && text == NULL) {
2725562Sjp161948 xfree(conv_ctxt->prompts);
2735562Sjp161948 xfree(conv_ctxt->responses);
2740Sstevel@tonic-gate xfree(conv_ctxt);
2750Sstevel@tonic-gate return PAM_SUCCESS;
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate authctxt->method->method_data = (void *) conv_ctxt;
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
2810Sstevel@tonic-gate packet_put_cstring(""); /* Name */
282*9600SNobutomo.Nakano@Sun.COM packet_put_utf8_cstring(text ? text : ""); /* Instructions */
2830Sstevel@tonic-gate packet_put_cstring(""); /* Language */
2840Sstevel@tonic-gate packet_put_int(conv_ctxt->num_expected);
2850Sstevel@tonic-gate
2860Sstevel@tonic-gate if (text)
2870Sstevel@tonic-gate xfree(text);
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate for (i = 0, j = 0; i < num_msg; i++) {
2900Sstevel@tonic-gate int style = PAM_MSG_MEMBER(msg, i, msg_style);
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate /* Skip messages which don't need a reply */
2930Sstevel@tonic-gate if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF)
2940Sstevel@tonic-gate continue;
2950Sstevel@tonic-gate
2960Sstevel@tonic-gate conv_ctxt->prompts[j++] = i;
297*9600SNobutomo.Nakano@Sun.COM packet_put_utf8_cstring(PAM_MSG_MEMBER(msg, i, msg));
2980Sstevel@tonic-gate packet_put_char(style == PAM_PROMPT_ECHO_ON);
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate packet_send();
3010Sstevel@tonic-gate packet_write_wait();
3020Sstevel@tonic-gate
3030Sstevel@tonic-gate /*
3040Sstevel@tonic-gate * Here the dispatch_run() loop is nested. It should be unwound
3050Sstevel@tonic-gate * if keyboard-interactive userauth is abandoned (or restarted;
3060Sstevel@tonic-gate * same thing).
3070Sstevel@tonic-gate *
3080Sstevel@tonic-gate * The condition for breaking out of the nested dispatch_run() loop is
3090Sstevel@tonic-gate * ((got kbd-int info reponse) || (kbd-int abandoned))
3100Sstevel@tonic-gate *
3110Sstevel@tonic-gate * conv_ctxt->finished is set in either of those cases.
3120Sstevel@tonic-gate *
3130Sstevel@tonic-gate * When abandonment is detected the conv_ctxt->finished is set as
3140Sstevel@tonic-gate * is conv_ctxt->abandoned, causing this function to signal
3150Sstevel@tonic-gate * userauth nested dispatch_run() loop unwinding and to return
3160Sstevel@tonic-gate * PAM_CONV_ERR;
3170Sstevel@tonic-gate */
3180Sstevel@tonic-gate debug2("Nesting dispatch_run loop");
3190Sstevel@tonic-gate dispatch_run(DISPATCH_BLOCK, &conv_ctxt->finished, appdata_ptr);
3200Sstevel@tonic-gate debug2("Nested dispatch_run loop exited");
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate if (conv_ctxt->abandoned) {
3230Sstevel@tonic-gate authctxt->unwind_dispatch_loop = 1;
3245562Sjp161948 xfree(conv_ctxt->prompts);
3255562Sjp161948 xfree(conv_ctxt->responses);
3260Sstevel@tonic-gate xfree(conv_ctxt);
3270Sstevel@tonic-gate debug("PAM conv function returns PAM_CONV_ERR");
3280Sstevel@tonic-gate return PAM_CONV_ERR;
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate if (conv_ctxt->num_received == conv_ctxt->num_expected) {
3320Sstevel@tonic-gate *resp = conv_ctxt->responses;
3335562Sjp161948 xfree(conv_ctxt->prompts);
3340Sstevel@tonic-gate xfree(conv_ctxt);
3350Sstevel@tonic-gate debug("PAM conv function returns PAM_SUCCESS");
3360Sstevel@tonic-gate return PAM_SUCCESS;
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate
3390Sstevel@tonic-gate debug("PAM conv function returns PAM_CONV_ERR");
3405562Sjp161948 xfree(conv_ctxt->prompts);
3415562Sjp161948 xfree(conv_ctxt->responses);
3420Sstevel@tonic-gate xfree(conv_ctxt);
3430Sstevel@tonic-gate return PAM_CONV_ERR;
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate static void
input_userauth_info_response_pam(int type,u_int32_t seqnr,void * ctxt)3470Sstevel@tonic-gate input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt)
3480Sstevel@tonic-gate {
3490Sstevel@tonic-gate Authctxt *authctxt = ctxt;
3500Sstevel@tonic-gate Convctxt *conv_ctxt;
3510Sstevel@tonic-gate unsigned int nresp = 0, rlen = 0, i = 0;
3520Sstevel@tonic-gate char *resp;
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate if (authctxt == NULL)
3550Sstevel@tonic-gate fatal("input_userauth_info_response_pam: no authentication context");
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate /* Check for spurious/unexpected info response */
3580Sstevel@tonic-gate if (method_kbdint.method_data == NULL) {
3590Sstevel@tonic-gate debug("input_userauth_info_response_pam: no method context");
3600Sstevel@tonic-gate return;
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate
3630Sstevel@tonic-gate conv_ctxt = (Convctxt *) method_kbdint.method_data;
3640Sstevel@tonic-gate
3650Sstevel@tonic-gate nresp = packet_get_int(); /* Number of responses. */
3660Sstevel@tonic-gate debug("got %d responses", nresp);
3670Sstevel@tonic-gate
3680Sstevel@tonic-gate
3690Sstevel@tonic-gate #if 0
3700Sstevel@tonic-gate if (nresp != conv_ctxt->num_expected)
3710Sstevel@tonic-gate fatal("%s: Received incorrect number of responses "
3720Sstevel@tonic-gate "(expected %d, received %u)", __func__,
3730Sstevel@tonic-gate conv_ctxt->num_expected, nresp);
3740Sstevel@tonic-gate #endif
3750Sstevel@tonic-gate
3760Sstevel@tonic-gate if (nresp > 100)
3770Sstevel@tonic-gate fatal("%s: too many replies", __func__);
3780Sstevel@tonic-gate
3790Sstevel@tonic-gate for (i = 0; i < nresp && i < conv_ctxt->num_expected ; i++) {
3800Sstevel@tonic-gate int j = conv_ctxt->prompts[i];
3810Sstevel@tonic-gate
382*9600SNobutomo.Nakano@Sun.COM /*
383*9600SNobutomo.Nakano@Sun.COM * We assume that ASCII charset is used for password
384*9600SNobutomo.Nakano@Sun.COM * although the protocol requires UTF-8 encoding for the
385*9600SNobutomo.Nakano@Sun.COM * password string. Therefore, we don't perform code
386*9600SNobutomo.Nakano@Sun.COM * conversion for the string.
387*9600SNobutomo.Nakano@Sun.COM */
3880Sstevel@tonic-gate resp = packet_get_string(&rlen);
3890Sstevel@tonic-gate if (i < conv_ctxt->num_expected) {
3900Sstevel@tonic-gate conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS;
3910Sstevel@tonic-gate conv_ctxt->responses[j].resp = xstrdup(resp);
3920Sstevel@tonic-gate conv_ctxt->num_received++;
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate xfree(resp);
3950Sstevel@tonic-gate }
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate if (nresp < conv_ctxt->num_expected)
3984775Svk199839 fatal("%s: too few replies (%d < %d)", __func__,
3994775Svk199839 nresp, conv_ctxt->num_expected);
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate /* XXX - This could make a covert channel... */
4020Sstevel@tonic-gate if (nresp > conv_ctxt->num_expected)
4030Sstevel@tonic-gate debug("Ignoring additional PAM replies");
4040Sstevel@tonic-gate
4050Sstevel@tonic-gate conv_ctxt->finished = 1;
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate packet_check_eom();
4080Sstevel@tonic-gate }
4090Sstevel@tonic-gate
4100Sstevel@tonic-gate #if 0
4110Sstevel@tonic-gate int
4120Sstevel@tonic-gate kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method)
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate if (!method)
4150Sstevel@tonic-gate return 0; /* fatal(), really; it'll happen somewhere else */
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate if (!method->method_data)
4180Sstevel@tonic-gate return 0;
4190Sstevel@tonic-gate
4200Sstevel@tonic-gate return 1;
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate #endif
4230Sstevel@tonic-gate
4240Sstevel@tonic-gate void
kbdint_pam_abandon(Authctxt * authctxt,Authmethod * method)4250Sstevel@tonic-gate kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method)
4260Sstevel@tonic-gate {
4270Sstevel@tonic-gate Convctxt *conv_ctxt;
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate /*
4300Sstevel@tonic-gate * But, if it ever becomes desirable and possible to support
4310Sstevel@tonic-gate * kbd-int userauth abandonment, here's what must be done.
4320Sstevel@tonic-gate */
4330Sstevel@tonic-gate if (!method)
4340Sstevel@tonic-gate return;
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate if (!method->method_data)
4370Sstevel@tonic-gate return;
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate conv_ctxt = (Convctxt *) method->method_data;
4400Sstevel@tonic-gate
4410Sstevel@tonic-gate /* dispatch_run() loop will exit */
4420Sstevel@tonic-gate conv_ctxt->abandoned = 1;
4430Sstevel@tonic-gate conv_ctxt->finished = 1;
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate /*
4460Sstevel@tonic-gate * The method_data will be free in the corresponding, active
4470Sstevel@tonic-gate * conversation function
4480Sstevel@tonic-gate */
4490Sstevel@tonic-gate method->method_data = NULL;
4500Sstevel@tonic-gate
4510Sstevel@tonic-gate /* update counts that can't be updated elsewhere */
4520Sstevel@tonic-gate method->abandons++;
4530Sstevel@tonic-gate method->attempts++;
4540Sstevel@tonic-gate
4550Sstevel@tonic-gate /* Finally, we cannot re-use the current current PAM handle */
4560Sstevel@tonic-gate authctxt->pam->h = NULL; /* Let the conv function cleanup */
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate #endif
459