109958426SBrian Feldman /* 209958426SBrian Feldman * Copyright (c) 2000 Damien Miller. All rights reserved. 309958426SBrian Feldman * 409958426SBrian Feldman * Redistribution and use in source and binary forms, with or without 509958426SBrian Feldman * modification, are permitted provided that the following conditions 609958426SBrian Feldman * are met: 709958426SBrian Feldman * 1. Redistributions of source code must retain the above copyright 809958426SBrian Feldman * notice, this list of conditions and the following disclaimer. 909958426SBrian Feldman * 2. Redistributions in binary form must reproduce the above copyright 1009958426SBrian Feldman * notice, this list of conditions and the following disclaimer in the 1109958426SBrian Feldman * documentation and/or other materials provided with the distribution. 1209958426SBrian Feldman * 1309958426SBrian Feldman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1409958426SBrian Feldman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1509958426SBrian Feldman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1609958426SBrian Feldman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1709958426SBrian Feldman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1809958426SBrian Feldman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1909958426SBrian Feldman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2009958426SBrian Feldman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2109958426SBrian Feldman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2209958426SBrian Feldman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2309958426SBrian Feldman */ 2409958426SBrian Feldman 2509958426SBrian Feldman #include "includes.h" 2609958426SBrian Feldman 2709958426SBrian Feldman #ifdef USE_PAM 2809958426SBrian Feldman #include <security/pam_appl.h> 2909958426SBrian Feldman #include "ssh.h" 3009958426SBrian Feldman #include "xmalloc.h" 3109958426SBrian Feldman #include "servconf.h" 3209958426SBrian Feldman 3309958426SBrian Feldman RCSID("$FreeBSD$"); 3409958426SBrian Feldman 3509958426SBrian Feldman #define NEW_AUTHTOK_MSG \ 3609958426SBrian Feldman "Warning: Your password has expired, please change it now" 3709958426SBrian Feldman 3809958426SBrian Feldman #define SSHD_PAM_SERVICE "sshd" 3909958426SBrian Feldman #define PAM_STRERROR(a, b) pam_strerror((a), (b)) 4009958426SBrian Feldman 4109958426SBrian Feldman /* Callbacks */ 4209958426SBrian Feldman static int pamconv(int num_msg, const struct pam_message **msg, 4309958426SBrian Feldman struct pam_response **resp, void *appdata_ptr); 4409958426SBrian Feldman void pam_cleanup_proc(void *context); 4509958426SBrian Feldman void pam_msg_cat(const char *msg); 4609958426SBrian Feldman 4709958426SBrian Feldman /* module-local variables */ 4809958426SBrian Feldman static struct pam_conv conv = { 4909958426SBrian Feldman pamconv, 5009958426SBrian Feldman NULL 5109958426SBrian Feldman }; 5209958426SBrian Feldman static pam_handle_t *pamh = NULL; 5309958426SBrian Feldman static const char *pampasswd = NULL; 5409958426SBrian Feldman static char *pam_msg = NULL; 5509958426SBrian Feldman 5609958426SBrian Feldman /* states for pamconv() */ 5709958426SBrian Feldman typedef enum { INITIAL_LOGIN, OTHER } pamstates; 5809958426SBrian Feldman static pamstates pamstate = INITIAL_LOGIN; 5909958426SBrian Feldman /* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */ 6009958426SBrian Feldman static int password_change_required = 0; 6109958426SBrian Feldman 6209958426SBrian Feldman /* 6309958426SBrian Feldman * PAM conversation function. 6409958426SBrian Feldman * There are two states this can run in. 6509958426SBrian Feldman * 6609958426SBrian Feldman * INITIAL_LOGIN mode simply feeds the password from the client into 6709958426SBrian Feldman * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output 6809958426SBrian Feldman * messages with pam_msg_cat(). This is used during initial 6909958426SBrian Feldman * authentication to bypass the normal PAM password prompt. 7009958426SBrian Feldman * 7109958426SBrian Feldman * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1) 7209958426SBrian Feldman * and outputs messages to stderr. This mode is used if pam_chauthtok() 7309958426SBrian Feldman * is called to update expired passwords. 7409958426SBrian Feldman */ 7509958426SBrian Feldman static int pamconv(int num_msg, const struct pam_message **msg, 7609958426SBrian Feldman struct pam_response **resp, void *appdata_ptr) 7709958426SBrian Feldman { 7809958426SBrian Feldman struct pam_response *reply; 7909958426SBrian Feldman int count; 8009958426SBrian Feldman char buf[1024]; 8109958426SBrian Feldman 8209958426SBrian Feldman /* PAM will free this later */ 8309958426SBrian Feldman reply = malloc(num_msg * sizeof(*reply)); 8409958426SBrian Feldman if (reply == NULL) 8509958426SBrian Feldman return PAM_CONV_ERR; 8609958426SBrian Feldman 8709958426SBrian Feldman for (count = 0; count < num_msg; count++) { 8809958426SBrian Feldman switch ((*msg)[count].msg_style) { 8909958426SBrian Feldman case PAM_PROMPT_ECHO_ON: 9009958426SBrian Feldman if (pamstate == INITIAL_LOGIN) { 9109958426SBrian Feldman free(reply); 9209958426SBrian Feldman return PAM_CONV_ERR; 9309958426SBrian Feldman } else { 9409958426SBrian Feldman fputs((*msg)[count].msg, stderr); 9509958426SBrian Feldman fgets(buf, sizeof(buf), stdin); 9609958426SBrian Feldman reply[count].resp = xstrdup(buf); 9709958426SBrian Feldman reply[count].resp_retcode = PAM_SUCCESS; 9809958426SBrian Feldman break; 9909958426SBrian Feldman } 10009958426SBrian Feldman case PAM_PROMPT_ECHO_OFF: 10109958426SBrian Feldman if (pamstate == INITIAL_LOGIN) { 10209958426SBrian Feldman if (pampasswd == NULL) { 10309958426SBrian Feldman free(reply); 10409958426SBrian Feldman return PAM_CONV_ERR; 10509958426SBrian Feldman } 10609958426SBrian Feldman reply[count].resp = xstrdup(pampasswd); 10709958426SBrian Feldman } else { 10809958426SBrian Feldman reply[count].resp = 10909958426SBrian Feldman xstrdup(read_passphrase((*msg)[count].msg, 1)); 11009958426SBrian Feldman } 11109958426SBrian Feldman reply[count].resp_retcode = PAM_SUCCESS; 11209958426SBrian Feldman break; 11309958426SBrian Feldman case PAM_ERROR_MSG: 11409958426SBrian Feldman case PAM_TEXT_INFO: 11509958426SBrian Feldman if ((*msg)[count].msg != NULL) { 11609958426SBrian Feldman if (pamstate == INITIAL_LOGIN) 11709958426SBrian Feldman pam_msg_cat((*msg)[count].msg); 11809958426SBrian Feldman else { 11909958426SBrian Feldman fputs((*msg)[count].msg, stderr); 12009958426SBrian Feldman fputs("\n", stderr); 12109958426SBrian Feldman } 12209958426SBrian Feldman } 12309958426SBrian Feldman reply[count].resp = xstrdup(""); 12409958426SBrian Feldman reply[count].resp_retcode = PAM_SUCCESS; 12509958426SBrian Feldman break; 12609958426SBrian Feldman default: 12709958426SBrian Feldman free(reply); 12809958426SBrian Feldman return PAM_CONV_ERR; 12909958426SBrian Feldman } 13009958426SBrian Feldman } 13109958426SBrian Feldman 13209958426SBrian Feldman *resp = reply; 13309958426SBrian Feldman 13409958426SBrian Feldman return PAM_SUCCESS; 13509958426SBrian Feldman } 13609958426SBrian Feldman 13709958426SBrian Feldman /* Called at exit to cleanly shutdown PAM */ 13809958426SBrian Feldman void pam_cleanup_proc(void *context) 13909958426SBrian Feldman { 14009958426SBrian Feldman int pam_retval; 14109958426SBrian Feldman 14209958426SBrian Feldman if (pamh != NULL) 14309958426SBrian Feldman { 14409958426SBrian Feldman pam_retval = pam_close_session(pamh, 0); 14509958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 14609958426SBrian Feldman log("Cannot close PAM session[%d]: %.200s", 14709958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 14809958426SBrian Feldman } 14909958426SBrian Feldman 15009958426SBrian Feldman pam_retval = pam_setcred(pamh, PAM_DELETE_CRED); 15109958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 15209958426SBrian Feldman debug("Cannot delete credentials[%d]: %.200s", 15309958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 15409958426SBrian Feldman } 15509958426SBrian Feldman 15609958426SBrian Feldman pam_retval = pam_end(pamh, pam_retval); 15709958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 15809958426SBrian Feldman log("Cannot release PAM authentication[%d]: %.200s", 15909958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 16009958426SBrian Feldman } 16109958426SBrian Feldman } 16209958426SBrian Feldman } 16309958426SBrian Feldman 16409958426SBrian Feldman /* Attempt password authentation using PAM */ 16509958426SBrian Feldman int auth_pam_password(struct passwd *pw, const char *password) 16609958426SBrian Feldman { 16709958426SBrian Feldman extern ServerOptions options; 16809958426SBrian Feldman int pam_retval; 16909958426SBrian Feldman 17009958426SBrian Feldman /* deny if no user. */ 17109958426SBrian Feldman if (pw == NULL) 17209958426SBrian Feldman return 0; 17309958426SBrian Feldman if (pw->pw_uid == 0 && options.permit_root_login == 2) 17409958426SBrian Feldman return 0; 17509958426SBrian Feldman if (*password == '\0' && options.permit_empty_passwd == 0) 17609958426SBrian Feldman return 0; 17709958426SBrian Feldman 17809958426SBrian Feldman pampasswd = password; 17909958426SBrian Feldman 18009958426SBrian Feldman pamstate = INITIAL_LOGIN; 18109958426SBrian Feldman pam_retval = pam_authenticate(pamh, 0); 18209958426SBrian Feldman if (pam_retval == PAM_SUCCESS) { 18309958426SBrian Feldman debug("PAM Password authentication accepted for user \"%.100s\"", 18409958426SBrian Feldman pw->pw_name); 18509958426SBrian Feldman return 1; 18609958426SBrian Feldman } else { 18709958426SBrian Feldman debug("PAM Password authentication for \"%.100s\" failed[%d]: %s", 18809958426SBrian Feldman pw->pw_name, pam_retval, PAM_STRERROR(pamh, pam_retval)); 18909958426SBrian Feldman return 0; 19009958426SBrian Feldman } 19109958426SBrian Feldman } 19209958426SBrian Feldman 19309958426SBrian Feldman /* Do account management using PAM */ 19409958426SBrian Feldman int do_pam_account(char *username, char *remote_user) 19509958426SBrian Feldman { 19609958426SBrian Feldman int pam_retval; 19709958426SBrian Feldman 19809958426SBrian Feldman debug("PAM setting rhost to \"%.200s\"", get_canonical_hostname()); 19909958426SBrian Feldman pam_retval = pam_set_item(pamh, PAM_RHOST, 20009958426SBrian Feldman get_canonical_hostname()); 20109958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 20209958426SBrian Feldman fatal("PAM set rhost failed[%d]: %.200s", 20309958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 20409958426SBrian Feldman } 20509958426SBrian Feldman 20609958426SBrian Feldman if (remote_user != NULL) { 20709958426SBrian Feldman debug("PAM setting ruser to \"%.200s\"", remote_user); 20809958426SBrian Feldman pam_retval = pam_set_item(pamh, PAM_RUSER, remote_user); 20909958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 21009958426SBrian Feldman fatal("PAM set ruser failed[%d]: %.200s", 21109958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 21209958426SBrian Feldman } 21309958426SBrian Feldman } 21409958426SBrian Feldman 21509958426SBrian Feldman pam_retval = pam_acct_mgmt(pamh, 0); 21609958426SBrian Feldman switch (pam_retval) { 21709958426SBrian Feldman case PAM_SUCCESS: 21809958426SBrian Feldman /* This is what we want */ 21909958426SBrian Feldman break; 22009958426SBrian Feldman case PAM_NEW_AUTHTOK_REQD: 22109958426SBrian Feldman pam_msg_cat(NEW_AUTHTOK_MSG); 22209958426SBrian Feldman /* flag that password change is necessary */ 22309958426SBrian Feldman password_change_required = 1; 22409958426SBrian Feldman break; 22509958426SBrian Feldman default: 22609958426SBrian Feldman log("PAM rejected by account configuration[%d]: %.200s", 22709958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 22809958426SBrian Feldman return(0); 22909958426SBrian Feldman } 23009958426SBrian Feldman 23109958426SBrian Feldman return(1); 23209958426SBrian Feldman } 23309958426SBrian Feldman 23409958426SBrian Feldman /* Do PAM-specific session initialisation */ 23509958426SBrian Feldman void do_pam_session(char *username, const char *ttyname) 23609958426SBrian Feldman { 23709958426SBrian Feldman int pam_retval; 23809958426SBrian Feldman 23909958426SBrian Feldman if (ttyname != NULL) { 24009958426SBrian Feldman debug("PAM setting tty to \"%.200s\"", ttyname); 24109958426SBrian Feldman pam_retval = pam_set_item(pamh, PAM_TTY, ttyname); 24209958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 24309958426SBrian Feldman fatal("PAM set tty failed[%d]: %.200s", 24409958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 24509958426SBrian Feldman } 24609958426SBrian Feldman } 24709958426SBrian Feldman 24809958426SBrian Feldman debug("do_pam_session: euid %u, uid %u", geteuid(), getuid()); 24909958426SBrian Feldman pam_retval = pam_open_session(pamh, 0); 25009958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 25109958426SBrian Feldman fatal("PAM session setup failed[%d]: %.200s", 25209958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 25309958426SBrian Feldman } 25409958426SBrian Feldman } 25509958426SBrian Feldman 25609958426SBrian Feldman /* Set PAM credentials */ 25709958426SBrian Feldman void do_pam_setcred(void) 25809958426SBrian Feldman { 25909958426SBrian Feldman int pam_retval; 26009958426SBrian Feldman 26109958426SBrian Feldman debug("PAM establishing creds"); 26209958426SBrian Feldman pam_retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); 26309958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 26409958426SBrian Feldman fatal("PAM setcred failed[%d]: %.200s", 26509958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 26609958426SBrian Feldman } 26709958426SBrian Feldman } 26809958426SBrian Feldman 26909958426SBrian Feldman /* accessor function for file scope static variable */ 27009958426SBrian Feldman int pam_password_change_required(void) 27109958426SBrian Feldman { 27209958426SBrian Feldman return password_change_required; 27309958426SBrian Feldman } 27409958426SBrian Feldman 27509958426SBrian Feldman /* 27609958426SBrian Feldman * Have user change authentication token if pam_acct_mgmt() indicated 27709958426SBrian Feldman * it was expired. This needs to be called after an interactive 27809958426SBrian Feldman * session is established and the user's pty is connected to 27909958426SBrian Feldman * stdin/stout/stderr. 28009958426SBrian Feldman */ 28109958426SBrian Feldman void do_pam_chauthtok(void) 28209958426SBrian Feldman { 28309958426SBrian Feldman int pam_retval; 28409958426SBrian Feldman 28509958426SBrian Feldman if (password_change_required) { 28609958426SBrian Feldman pamstate = OTHER; 28709958426SBrian Feldman /* 28809958426SBrian Feldman * XXX: should we really loop forever? 28909958426SBrian Feldman */ 29009958426SBrian Feldman do { 29109958426SBrian Feldman pam_retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 29209958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 29309958426SBrian Feldman log("PAM pam_chauthtok failed[%d]: %.200s", 29409958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 29509958426SBrian Feldman } 29609958426SBrian Feldman } while (pam_retval != PAM_SUCCESS); 29709958426SBrian Feldman } 29809958426SBrian Feldman } 29909958426SBrian Feldman 30009958426SBrian Feldman /* Cleanly shutdown PAM */ 30109958426SBrian Feldman void finish_pam(void) 30209958426SBrian Feldman { 30309958426SBrian Feldman pam_cleanup_proc(NULL); 30409958426SBrian Feldman fatal_remove_cleanup(&pam_cleanup_proc, NULL); 30509958426SBrian Feldman } 30609958426SBrian Feldman 30709958426SBrian Feldman /* Start PAM authentication for specified account */ 30809958426SBrian Feldman void start_pam(struct passwd *pw) 30909958426SBrian Feldman { 31009958426SBrian Feldman int pam_retval; 31109958426SBrian Feldman 31209958426SBrian Feldman debug("Starting up PAM with username \"%.200s\"", pw->pw_name); 31309958426SBrian Feldman 31409958426SBrian Feldman pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, &pamh); 31509958426SBrian Feldman 31609958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 31709958426SBrian Feldman fatal("PAM initialisation failed[%d]: %.200s", 31809958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 31909958426SBrian Feldman } 32009958426SBrian Feldman 32109958426SBrian Feldman #ifdef PAM_TTY_KLUDGE 32209958426SBrian Feldman /* 32309958426SBrian Feldman * Some PAM modules (e.g. pam_time) require a TTY to operate, 32409958426SBrian Feldman * and will fail in various stupid ways if they don't get one. 32509958426SBrian Feldman * sshd doesn't set the tty until too late in the auth process and may 32609958426SBrian Feldman * not even need one (for tty-less connections) 32709958426SBrian Feldman * Kludge: Set a fake PAM_TTY 32809958426SBrian Feldman */ 32909958426SBrian Feldman pam_retval = pam_set_item(pamh, PAM_TTY, "ssh"); 33009958426SBrian Feldman if (pam_retval != PAM_SUCCESS) { 33109958426SBrian Feldman fatal("PAM set tty failed[%d]: %.200s", 33209958426SBrian Feldman pam_retval, PAM_STRERROR(pamh, pam_retval)); 33309958426SBrian Feldman } 33409958426SBrian Feldman #endif /* PAM_TTY_KLUDGE */ 33509958426SBrian Feldman 33609958426SBrian Feldman fatal_add_cleanup(&pam_cleanup_proc, NULL); 33709958426SBrian Feldman } 33809958426SBrian Feldman 33909958426SBrian Feldman /* Return list of PAM enviornment strings */ 34009958426SBrian Feldman char **fetch_pam_environment(void) 34109958426SBrian Feldman { 34209958426SBrian Feldman #ifdef HAVE_PAM_GETENVLIST 34309958426SBrian Feldman return(pam_getenvlist(pamh)); 34409958426SBrian Feldman #else /* HAVE_PAM_GETENVLIST */ 34509958426SBrian Feldman return(NULL); 34609958426SBrian Feldman #endif /* HAVE_PAM_GETENVLIST */ 34709958426SBrian Feldman } 34809958426SBrian Feldman 34909958426SBrian Feldman /* Print any messages that have been generated during authentication */ 35009958426SBrian Feldman /* or account checking to stderr */ 35109958426SBrian Feldman void print_pam_messages(void) 35209958426SBrian Feldman { 35309958426SBrian Feldman if (pam_msg != NULL) 35409958426SBrian Feldman fputs(pam_msg, stderr); 35509958426SBrian Feldman } 35609958426SBrian Feldman 35709958426SBrian Feldman /* Append a message to the PAM message buffer */ 35809958426SBrian Feldman void pam_msg_cat(const char *msg) 35909958426SBrian Feldman { 36009958426SBrian Feldman char *p; 36109958426SBrian Feldman size_t new_msg_len; 36209958426SBrian Feldman size_t pam_msg_len; 36309958426SBrian Feldman 36409958426SBrian Feldman new_msg_len = strlen(msg); 36509958426SBrian Feldman 36609958426SBrian Feldman if (pam_msg) { 36709958426SBrian Feldman pam_msg_len = strlen(pam_msg); 36809958426SBrian Feldman pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2); 36909958426SBrian Feldman p = pam_msg + pam_msg_len; 37009958426SBrian Feldman } else { 37109958426SBrian Feldman pam_msg = p = xmalloc(new_msg_len + 2); 37209958426SBrian Feldman } 37309958426SBrian Feldman 37409958426SBrian Feldman memcpy(p, msg, new_msg_len); 37509958426SBrian Feldman p[new_msg_len] = '\n'; 37609958426SBrian Feldman p[new_msg_len + 1] = '\0'; 37709958426SBrian Feldman } 37809958426SBrian Feldman 37909958426SBrian Feldman struct inverted_pam_userdata { 38009958426SBrian Feldman /* 38109958426SBrian Feldman * Pipe for telling whether we are doing conversation or sending 38209958426SBrian Feldman * authentication results. 38309958426SBrian Feldman */ 38409958426SBrian Feldman int statefd[2]; 38509958426SBrian Feldman int challengefd[2]; 38609958426SBrian Feldman int responsefd[2]; 38709958426SBrian Feldman 38809958426SBrian Feldman /* Whether we have sent off our challenge */ 38909958426SBrian Feldman int state; 39009958426SBrian Feldman }; 39109958426SBrian Feldman 39209958426SBrian Feldman #define STATE_CONV 1 39309958426SBrian Feldman #define STATE_AUTH_OK 2 39409958426SBrian Feldman #define STATE_AUTH_FAIL 3 39509958426SBrian Feldman 39609958426SBrian Feldman int 39709958426SBrian Feldman ssh_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, 39809958426SBrian Feldman void *userdata) { 39909958426SBrian Feldman int i; 40009958426SBrian Feldman FILE *reader; 40109958426SBrian Feldman char buf[1024]; 40209958426SBrian Feldman struct pam_response *reply = NULL; 40309958426SBrian Feldman char state_to_write = STATE_CONV; /* One char to write */ 40409958426SBrian Feldman struct inverted_pam_userdata *ud = userdata; 40509958426SBrian Feldman char *response = NULL; 40609958426SBrian Feldman 40709958426SBrian Feldman /* The stdio functions are more convenient for the read half */ 40809958426SBrian Feldman reader = fdopen(ud->responsefd[0], "rb"); 40909958426SBrian Feldman if (reader == NULL) 41009958426SBrian Feldman goto protocol_failure; 41109958426SBrian Feldman 41209958426SBrian Feldman reply = malloc(num_msg * sizeof(struct pam_response)); 41309958426SBrian Feldman if (reply == NULL) 41409958426SBrian Feldman return PAM_CONV_ERR; 41509958426SBrian Feldman 41609958426SBrian Feldman if (write(ud->statefd[1], &state_to_write, 1) != 1) 41709958426SBrian Feldman goto protocol_failure; 41809958426SBrian Feldman 41909958426SBrian Feldman /* 42009958426SBrian Feldman * Re-package our data and send it off to our better half (the actual SSH 42109958426SBrian Feldman * process) 42209958426SBrian Feldman */ 42309958426SBrian Feldman if (write(ud->challengefd[1], buf, 42409958426SBrian Feldman sprintf(buf, "%d\n", num_msg)) == -1) 42509958426SBrian Feldman goto protocol_failure; 42609958426SBrian Feldman for (i = 0; i < num_msg; i++) { 42709958426SBrian Feldman if (write(ud->challengefd[1], buf, 42809958426SBrian Feldman sprintf(buf, "%d\n", msg[i]->msg_style)) == -1) 42909958426SBrian Feldman goto protocol_failure; 43009958426SBrian Feldman if (write(ud->challengefd[1], buf, 43109958426SBrian Feldman sprintf(buf, "%d\n", strlen(msg[i]->msg))) == -1) 43209958426SBrian Feldman goto protocol_failure; 43309958426SBrian Feldman if (write(ud->challengefd[1], msg[i]->msg, 43409958426SBrian Feldman strlen(msg[i]->msg)) == -1) 43509958426SBrian Feldman goto protocol_failure; 43609958426SBrian Feldman } 43709958426SBrian Feldman /* 43809958426SBrian Feldman * Read back responses. These may not be as nice as we want, as the SSH 43909958426SBrian Feldman * protocol isn't exactly a perfect fit with PAM. 44009958426SBrian Feldman */ 44109958426SBrian Feldman 44209958426SBrian Feldman for (i = 0; i < num_msg; i++) { 44309958426SBrian Feldman char buf[1024]; 44409958426SBrian Feldman char *endptr; 44509958426SBrian Feldman size_t len; /* Length of the response */ 44609958426SBrian Feldman 44709958426SBrian Feldman switch (msg[i]->msg_style) { 44809958426SBrian Feldman case PAM_PROMPT_ECHO_OFF: 44909958426SBrian Feldman case PAM_PROMPT_ECHO_ON: 45009958426SBrian Feldman if (fgets(buf, sizeof(buf), reader) == NULL) 45109958426SBrian Feldman goto protocol_failure; 45209958426SBrian Feldman len = (size_t)strtoul(buf, &endptr, 10); 45309958426SBrian Feldman /* The length is supposed to stand on a line by itself */ 45409958426SBrian Feldman if (endptr == NULL || *endptr != '\n') 45509958426SBrian Feldman goto protocol_failure; 45609958426SBrian Feldman response = malloc(len+1); 45709958426SBrian Feldman if (response == NULL) 45809958426SBrian Feldman goto protocol_failure; 45909958426SBrian Feldman if (fread(response, len, 1, reader) != 1) 46009958426SBrian Feldman goto protocol_failure; 46109958426SBrian Feldman response[len] = '\0'; 46209958426SBrian Feldman reply[i].resp = response; 46309958426SBrian Feldman response = NULL; 46409958426SBrian Feldman break; 46509958426SBrian Feldman default: 46609958426SBrian Feldman reply[i].resp = NULL; 46709958426SBrian Feldman break; 46809958426SBrian Feldman } 46909958426SBrian Feldman } 47009958426SBrian Feldman *resp = reply; 47109958426SBrian Feldman return PAM_SUCCESS; 47209958426SBrian Feldman protocol_failure: 47309958426SBrian Feldman free(reply); 47409958426SBrian Feldman return PAM_CONV_ERR; 47509958426SBrian Feldman } 47609958426SBrian Feldman 47709958426SBrian Feldman void 47809958426SBrian Feldman ipam_free_cookie(struct inverted_pam_cookie *cookie) { 47909958426SBrian Feldman struct inverted_pam_userdata *ud; 48009958426SBrian Feldman int i; 48109958426SBrian Feldman 48209958426SBrian Feldman if (cookie == NULL) 48309958426SBrian Feldman return; 48409958426SBrian Feldman ud = cookie->userdata; 48509958426SBrian Feldman cookie->userdata = NULL; 48609958426SBrian Feldman /* Free userdata if allocated */ 48709958426SBrian Feldman if (ud) { 48809958426SBrian Feldman /* Close any opened file descriptors */ 48909958426SBrian Feldman if (ud->statefd[0] != -1) 49009958426SBrian Feldman close(ud->statefd[0]); 49109958426SBrian Feldman if (ud->statefd[1] != -1) 49209958426SBrian Feldman close(ud->statefd[1]); 49309958426SBrian Feldman if (ud->challengefd[0] != -1) 49409958426SBrian Feldman close(ud->challengefd[0]); 49509958426SBrian Feldman if (ud->challengefd[1] != -1) 49609958426SBrian Feldman close(ud->challengefd[1]); 49709958426SBrian Feldman if (ud->responsefd[0] != -1) 49809958426SBrian Feldman close(ud->responsefd[0]); 49909958426SBrian Feldman if (ud->responsefd[1] != -1) 50009958426SBrian Feldman close(ud->responsefd[1]); 50109958426SBrian Feldman free(ud); 50209958426SBrian Feldman ud = NULL; 50309958426SBrian Feldman } 50409958426SBrian Feldman /* Now free the normal cookie */ 50509958426SBrian Feldman if (cookie->pid != 0 && cookie->pid != -1) { 50609958426SBrian Feldman int status; 50709958426SBrian Feldman 50809958426SBrian Feldman /* XXX Use different signal? */ 50909958426SBrian Feldman kill(cookie->pid, SIGKILL); 51009958426SBrian Feldman waitpid(cookie->pid, &status, 0); 51109958426SBrian Feldman } 51209958426SBrian Feldman for (i = 0; i < cookie->num_msg; i++) { 51309958426SBrian Feldman if (cookie->resp && cookie->resp[i]) { 51409958426SBrian Feldman free(cookie->resp[i]->resp); 51509958426SBrian Feldman free(cookie->resp[i]); 51609958426SBrian Feldman } 51709958426SBrian Feldman if (cookie->msg && cookie->msg[i]) { 51809958426SBrian Feldman free((void *)cookie->msg[i]->msg); 51909958426SBrian Feldman free(cookie->msg[i]); 52009958426SBrian Feldman } 52109958426SBrian Feldman } 52209958426SBrian Feldman free(cookie->msg); 52309958426SBrian Feldman free(cookie->resp); 52409958426SBrian Feldman free(cookie); 52509958426SBrian Feldman } 52609958426SBrian Feldman 52709958426SBrian Feldman /* 52809958426SBrian Feldman * Do first half of PAM authentication - this comes to the point where 52909958426SBrian Feldman * you get a message to send to the user. 53009958426SBrian Feldman */ 53109958426SBrian Feldman struct inverted_pam_cookie * 53209958426SBrian Feldman ipam_start_auth(const char *service, const char *username) { 53309958426SBrian Feldman struct inverted_pam_cookie *cookie; 53409958426SBrian Feldman struct inverted_pam_userdata *ud; 53509958426SBrian Feldman static struct pam_conv conv = { 53609958426SBrian Feldman ssh_conv, 53709958426SBrian Feldman NULL 53809958426SBrian Feldman }; 53909958426SBrian Feldman 54009958426SBrian Feldman cookie = malloc(sizeof(*cookie)); 54109958426SBrian Feldman if (cookie == NULL) 54209958426SBrian Feldman return NULL; 54309958426SBrian Feldman cookie->state = 0; 54409958426SBrian Feldman /* Set up the cookie so ipam_freecookie can be used on it */ 54509958426SBrian Feldman cookie->num_msg = 0; 54609958426SBrian Feldman cookie->msg = NULL; 54709958426SBrian Feldman cookie->resp = NULL; 54809958426SBrian Feldman cookie->pid = -1; 54909958426SBrian Feldman 55009958426SBrian Feldman ud = calloc(sizeof(*ud), 1); 55109958426SBrian Feldman if (ud == NULL) { 55209958426SBrian Feldman free(cookie); 55309958426SBrian Feldman return NULL; 55409958426SBrian Feldman } 55509958426SBrian Feldman cookie->userdata = ud; 55609958426SBrian Feldman ud->statefd[0] = ud->statefd[1] = -1; 55709958426SBrian Feldman ud->challengefd[0] = ud->challengefd[1] = -1; 55809958426SBrian Feldman ud->responsefd[0] = ud->responsefd[1] = -1; 55909958426SBrian Feldman 56009958426SBrian Feldman if (pipe(ud->statefd) != 0) { 56109958426SBrian Feldman ud->statefd[0] = ud->statefd[1] = -1; 56209958426SBrian Feldman ipam_free_cookie(cookie); 56309958426SBrian Feldman return NULL; 56409958426SBrian Feldman } 56509958426SBrian Feldman if (pipe(ud->challengefd) != 0) { 56609958426SBrian Feldman ud->challengefd[0] = ud->challengefd[1] = -1; 56709958426SBrian Feldman ipam_free_cookie(cookie); 56809958426SBrian Feldman return NULL; 56909958426SBrian Feldman } 57009958426SBrian Feldman if (pipe(ud->responsefd) != 0) { 57109958426SBrian Feldman ud->responsefd[0] = ud->responsefd[1] = -1; 57209958426SBrian Feldman ipam_free_cookie(cookie); 57309958426SBrian Feldman return NULL; 57409958426SBrian Feldman } 57509958426SBrian Feldman cookie->pid = fork(); 57609958426SBrian Feldman if (cookie->pid == -1) { 57709958426SBrian Feldman ipam_free_cookie(cookie); 57809958426SBrian Feldman return NULL; 57909958426SBrian Feldman } else if (cookie->pid != 0) { 58009958426SBrian Feldman int num_msgs; /* Number of messages from PAM */ 58109958426SBrian Feldman char *endptr; 58209958426SBrian Feldman char buf[1024]; 58309958426SBrian Feldman FILE *reader; 58409958426SBrian Feldman size_t num_msg; 58509958426SBrian Feldman int i; 58609958426SBrian Feldman char state; /* Which state did the connection just enter? */ 58709958426SBrian Feldman 58809958426SBrian Feldman /* We are the parent - wait for a call to the communications 58909958426SBrian Feldman function to turn up, or the challenge to be finished */ 59009958426SBrian Feldman if (read(ud->statefd[0], &state, 1) != 1) { 59109958426SBrian Feldman ipam_free_cookie(cookie); 59209958426SBrian Feldman return NULL; 59309958426SBrian Feldman } 59409958426SBrian Feldman cookie->state = state; 59509958426SBrian Feldman switch (state) { 59609958426SBrian Feldman case STATE_CONV: 59709958426SBrian Feldman /* We are running the conversation function */ 59809958426SBrian Feldman /* The stdio functions are more convenient for read */ 59909958426SBrian Feldman reader = fdopen(ud->challengefd[0], "r"); 60009958426SBrian Feldman if (reader == NULL) { 60109958426SBrian Feldman ipam_free_cookie(cookie); 60209958426SBrian Feldman return NULL; 60309958426SBrian Feldman } 60409958426SBrian Feldman if (fgets(buf, 4, reader) == NULL) { 60509958426SBrian Feldman fclose(reader); 60609958426SBrian Feldman ipam_free_cookie(cookie); 60709958426SBrian Feldman return NULL; 60809958426SBrian Feldman } 60909958426SBrian Feldman num_msg = (size_t)strtoul(buf, &endptr, 10); 61009958426SBrian Feldman /* The length is supposed to stand on a line by itself */ 61109958426SBrian Feldman if (endptr == NULL || *endptr != '\n') { 61209958426SBrian Feldman fclose(reader); 61309958426SBrian Feldman ipam_free_cookie(cookie); 61409958426SBrian Feldman return NULL; 61509958426SBrian Feldman } 61609958426SBrian Feldman cookie->msg = 61709958426SBrian Feldman malloc(sizeof(struct pam_message *) * num_msg); 61809958426SBrian Feldman cookie->resp = 61909958426SBrian Feldman malloc(sizeof(struct pam_response *) * num_msg); 62009958426SBrian Feldman if (cookie->msg == NULL || cookie->resp == NULL) { 62109958426SBrian Feldman fclose(reader); 62209958426SBrian Feldman ipam_free_cookie(cookie); 62309958426SBrian Feldman return NULL; 62409958426SBrian Feldman } 62509958426SBrian Feldman for (i = 0; i < num_msg; i++) { 62609958426SBrian Feldman cookie->msg[i] = 62709958426SBrian Feldman malloc(sizeof(struct pam_message)); 62809958426SBrian Feldman cookie->resp[i] = 62909958426SBrian Feldman malloc(sizeof(struct pam_response)); 63009958426SBrian Feldman if (cookie->msg[i] == NULL || 63109958426SBrian Feldman cookie->resp[i] == NULL) { 63209958426SBrian Feldman for (;;) { 63309958426SBrian Feldman free(cookie->msg[i]); 63409958426SBrian Feldman free(cookie->resp[i]); 63509958426SBrian Feldman if (i == 0) 63609958426SBrian Feldman break; 63709958426SBrian Feldman i--; 63809958426SBrian Feldman } 63909958426SBrian Feldman fclose(reader); 64009958426SBrian Feldman ipam_free_cookie(cookie); 64109958426SBrian Feldman return NULL; 64209958426SBrian Feldman } 64309958426SBrian Feldman cookie->msg[i]->msg = NULL; 64409958426SBrian Feldman cookie->resp[i]->resp = NULL; 64509958426SBrian Feldman cookie->resp[i]->resp_retcode = 0; 64609958426SBrian Feldman } 64709958426SBrian Feldman /* Set up so the above will be freed on failure */ 64809958426SBrian Feldman cookie->num_msg = num_msg; 64909958426SBrian Feldman /* 65009958426SBrian Feldman * We have a an allocated response and message for 65109958426SBrian Feldman * each of the entries in the PAM structure - transfer 65209958426SBrian Feldman * the data sent to the conversation function over. 65309958426SBrian Feldman */ 65409958426SBrian Feldman for (i = 0; i < num_msg; i++) { 65509958426SBrian Feldman size_t len; 65609958426SBrian Feldman 65709958426SBrian Feldman if (fgets(buf, sizeof(buf), reader) == NULL) { 65809958426SBrian Feldman fclose(reader); 65909958426SBrian Feldman ipam_free_cookie(cookie); 66009958426SBrian Feldman return NULL; 66109958426SBrian Feldman } 66209958426SBrian Feldman cookie->msg[i]->msg_style = 66309958426SBrian Feldman (size_t)strtoul(buf, &endptr, 10); 66409958426SBrian Feldman if (endptr == NULL || *endptr != '\n') { 66509958426SBrian Feldman fclose(reader); 66609958426SBrian Feldman ipam_free_cookie(cookie); 66709958426SBrian Feldman return NULL; 66809958426SBrian Feldman } 66909958426SBrian Feldman if (fgets(buf, sizeof(buf), reader) == NULL) { 67009958426SBrian Feldman fclose(reader); 67109958426SBrian Feldman ipam_free_cookie(cookie); 67209958426SBrian Feldman return NULL; 67309958426SBrian Feldman } 67409958426SBrian Feldman len = (size_t)strtoul(buf, &endptr, 10); 67509958426SBrian Feldman if (endptr == NULL || *endptr != '\n') { 67609958426SBrian Feldman fclose(reader); 67709958426SBrian Feldman ipam_free_cookie(cookie); 67809958426SBrian Feldman return NULL; 67909958426SBrian Feldman } 68009958426SBrian Feldman cookie->msg[i]->msg = malloc(len + 1); 68109958426SBrian Feldman if (cookie->msg[i]->msg == NULL) { 68209958426SBrian Feldman fclose(reader); 68309958426SBrian Feldman ipam_free_cookie(cookie); 68409958426SBrian Feldman return NULL; 68509958426SBrian Feldman } 68609958426SBrian Feldman if (fread((char *)cookie->msg[i]->msg, len, 1, reader) != 68709958426SBrian Feldman 1) { 68809958426SBrian Feldman fclose(reader); 68909958426SBrian Feldman ipam_free_cookie(cookie); 69009958426SBrian Feldman return NULL; 69109958426SBrian Feldman } 69209958426SBrian Feldman *(char *)&(cookie->msg[i]->msg[len]) = '\0'; 69309958426SBrian Feldman } 69409958426SBrian Feldman break; 69509958426SBrian Feldman case STATE_AUTH_OK: 69609958426SBrian Feldman case STATE_AUTH_FAIL: 69709958426SBrian Feldman break; 69809958426SBrian Feldman default: 69909958426SBrian Feldman /* Internal failure, somehow */ 70009958426SBrian Feldman fclose(reader); 70109958426SBrian Feldman ipam_free_cookie(cookie); 70209958426SBrian Feldman return NULL; 70309958426SBrian Feldman } 70409958426SBrian Feldman return cookie; 70509958426SBrian Feldman } else { 70609958426SBrian Feldman /* We are the child */ 70709958426SBrian Feldman pam_handle_t *pamh=NULL; 70809958426SBrian Feldman int retval; 70909958426SBrian Feldman char state; 71009958426SBrian Feldman 71109958426SBrian Feldman conv.appdata_ptr = ud; 71209958426SBrian Feldman retval = pam_start(service, username, &conv, &pamh); 71309958426SBrian Feldman /* Is user really user? */ 71409958426SBrian Feldman if (retval == PAM_SUCCESS) 71509958426SBrian Feldman retval = pam_authenticate(pamh, 0); 71609958426SBrian Feldman /* permitted access? */ 71709958426SBrian Feldman if (retval == PAM_SUCCESS) 71809958426SBrian Feldman retval = pam_acct_mgmt(pamh, 0); 71909958426SBrian Feldman /* This is where we have been authorized or not. */ 72009958426SBrian Feldman 72109958426SBrian Feldman /* Be conservative - flag as auth failure if we can't close */ 72209958426SBrian Feldman /* 72309958426SBrian Feldman * XXX This is based on example code from Linux-PAM - 72409958426SBrian Feldman * but can it really be correct to pam_end if 72509958426SBrian Feldman * pam_start failed? 72609958426SBrian Feldman */ 72709958426SBrian Feldman if (pam_end(pamh, retval) != PAM_SUCCESS) 72809958426SBrian Feldman retval = PAM_AUTH_ERR; 72909958426SBrian Feldman 73009958426SBrian Feldman /* Message to parent */ 73109958426SBrian Feldman state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL; 73209958426SBrian Feldman if (write(ud->statefd[1], &state, 1) != 1) { 73309958426SBrian Feldman _exit(1); 73409958426SBrian Feldman } 73509958426SBrian Feldman /* FDs will be closed, so further communication will stop */ 73609958426SBrian Feldman _exit(0); 73709958426SBrian Feldman } 73809958426SBrian Feldman } 73909958426SBrian Feldman 74009958426SBrian Feldman /* 74109958426SBrian Feldman * Do second half of PAM authentication - cookie should now be filled 74209958426SBrian Feldman * in with the response to the challenge. 74309958426SBrian Feldman */ 74409958426SBrian Feldman 74509958426SBrian Feldman int 74609958426SBrian Feldman ipam_complete_auth(struct inverted_pam_cookie *cookie) { 74709958426SBrian Feldman int i; 74809958426SBrian Feldman char buf[1024]; 74909958426SBrian Feldman struct inverted_pam_userdata *ud = cookie->userdata; 75009958426SBrian Feldman char state; 75109958426SBrian Feldman 75209958426SBrian Feldman /* Send over our responses */ 75309958426SBrian Feldman for (i = 0; i < cookie->num_msg; i++) { 75409958426SBrian Feldman if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON && 75509958426SBrian Feldman cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 75609958426SBrian Feldman continue; 75709958426SBrian Feldman if (write(ud->responsefd[1], buf, 75809958426SBrian Feldman sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) { 75909958426SBrian Feldman ipam_free_cookie(cookie); 76009958426SBrian Feldman return 0; 76109958426SBrian Feldman } 76209958426SBrian Feldman if (write(ud->responsefd[1], cookie->resp[i]->resp, 76309958426SBrian Feldman strlen(cookie->resp[i]->resp)) == -1) { 76409958426SBrian Feldman ipam_free_cookie(cookie); 76509958426SBrian Feldman return 0; 76609958426SBrian Feldman } 76709958426SBrian Feldman } 76809958426SBrian Feldman /* Find out what state we are changing to */ 76909958426SBrian Feldman if (read(ud->statefd[0], &state, 1) != 1) { 77009958426SBrian Feldman ipam_free_cookie(cookie); 77109958426SBrian Feldman return 0; 77209958426SBrian Feldman } 77309958426SBrian Feldman 77409958426SBrian Feldman return state == STATE_AUTH_OK ? 1 : 0; 77509958426SBrian Feldman } 77609958426SBrian Feldman 77709958426SBrian Feldman #endif /* USE_PAM */ 778