10Sstevel@tonic-gate /* 27095Sdarrenm * Copyright 2008 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" 37*8305SPeter.Shoults@Sun.COM /* Maximum attempts for changing expired password */ 38*8305SPeter.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 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 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 */ 1124775Svk199839 packet_put_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)"; 134*8305SPeter.Shoults@Sun.COM /* 135*8305SPeter.Shoults@Sun.COM * Depending on error returned from pam_chauthtok, we 136*8305SPeter.Shoults@Sun.COM * need to try to change password a few times before 137*8305SPeter.Shoults@Sun.COM * we error out and return. 138*8305SPeter.Shoults@Sun.COM */ 139*8305SPeter.Shoults@Sun.COM int tries = 0; 140*8305SPeter.Shoults@Sun.COM while ((retval = pam_chauthtok(pamh, 141*8305SPeter.Shoults@Sun.COM PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) { 142*8305SPeter.Shoults@Sun.COM if (tries++ < DEF_ATTEMPTS) { 143*8305SPeter.Shoults@Sun.COM if ((retval == PAM_AUTHTOK_ERR) || 144*8305SPeter.Shoults@Sun.COM (retval == PAM_TRY_AGAIN)) { 145*8305SPeter.Shoults@Sun.COM continue; 146*8305SPeter.Shoults@Sun.COM } 147*8305SPeter.Shoults@Sun.COM } 148*8305SPeter.Shoults@Sun.COM break; 149*8305SPeter.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 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 */ 2820Sstevel@tonic-gate packet_put_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; 2970Sstevel@tonic-gate packet_put_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 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 3820Sstevel@tonic-gate resp = packet_get_string(&rlen); 3830Sstevel@tonic-gate if (i < conv_ctxt->num_expected) { 3840Sstevel@tonic-gate conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS; 3850Sstevel@tonic-gate conv_ctxt->responses[j].resp = xstrdup(resp); 3860Sstevel@tonic-gate conv_ctxt->num_received++; 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate xfree(resp); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate if (nresp < conv_ctxt->num_expected) 3924775Svk199839 fatal("%s: too few replies (%d < %d)", __func__, 3934775Svk199839 nresp, conv_ctxt->num_expected); 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate /* XXX - This could make a covert channel... */ 3960Sstevel@tonic-gate if (nresp > conv_ctxt->num_expected) 3970Sstevel@tonic-gate debug("Ignoring additional PAM replies"); 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate conv_ctxt->finished = 1; 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate packet_check_eom(); 4020Sstevel@tonic-gate } 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate #if 0 4050Sstevel@tonic-gate int 4060Sstevel@tonic-gate kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method) 4070Sstevel@tonic-gate { 4080Sstevel@tonic-gate if (!method) 4090Sstevel@tonic-gate return 0; /* fatal(), really; it'll happen somewhere else */ 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate if (!method->method_data) 4120Sstevel@tonic-gate return 0; 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate return 1; 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate #endif 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate void 4190Sstevel@tonic-gate kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method) 4200Sstevel@tonic-gate { 4210Sstevel@tonic-gate Convctxt *conv_ctxt; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate /* 4240Sstevel@tonic-gate * But, if it ever becomes desirable and possible to support 4250Sstevel@tonic-gate * kbd-int userauth abandonment, here's what must be done. 4260Sstevel@tonic-gate */ 4270Sstevel@tonic-gate if (!method) 4280Sstevel@tonic-gate return; 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate if (!method->method_data) 4310Sstevel@tonic-gate return; 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate conv_ctxt = (Convctxt *) method->method_data; 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate /* dispatch_run() loop will exit */ 4360Sstevel@tonic-gate conv_ctxt->abandoned = 1; 4370Sstevel@tonic-gate conv_ctxt->finished = 1; 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate /* 4400Sstevel@tonic-gate * The method_data will be free in the corresponding, active 4410Sstevel@tonic-gate * conversation function 4420Sstevel@tonic-gate */ 4430Sstevel@tonic-gate method->method_data = NULL; 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate /* update counts that can't be updated elsewhere */ 4460Sstevel@tonic-gate method->abandons++; 4470Sstevel@tonic-gate method->attempts++; 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate /* Finally, we cannot re-use the current current PAM handle */ 4500Sstevel@tonic-gate authctxt->pam->h = NULL; /* Let the conv function cleanup */ 4510Sstevel@tonic-gate } 4520Sstevel@tonic-gate #endif 453