diff options
Diffstat (limited to 'crypto/openssh/auth-pam.c')
-rw-r--r-- | crypto/openssh/auth-pam.c | 733 |
1 files changed, 172 insertions, 561 deletions
diff --git a/crypto/openssh/auth-pam.c b/crypto/openssh/auth-pam.c index 1b6ee19..490990d 100644 --- a/crypto/openssh/auth-pam.c +++ b/crypto/openssh/auth-pam.c @@ -25,41 +25,36 @@ #include "includes.h" #ifdef USE_PAM -#include <security/pam_appl.h> #include "ssh.h" #include "xmalloc.h" #include "log.h" +#include "auth.h" +#include "auth-pam.h" #include "servconf.h" -#include "readpass.h" #include "canohost.h" +#include "readpass.h" + +extern char *__progname; -RCSID("$FreeBSD$"); +RCSID("$Id: auth-pam.c,v 1.46 2002/05/08 02:27:56 djm Exp $"); #define NEW_AUTHTOK_MSG \ "Warning: Your password has expired, please change it now" -#define SSHD_PAM_SERVICE "sshd" -#define PAM_STRERROR(a, b) pam_strerror((a), (b)) - -/* Callbacks */ static int do_pam_conversation(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr); -void do_pam_cleanup_proc(void *context); -void pam_msg_cat(const char *msg); + struct pam_response **resp, void *appdata_ptr); /* module-local variables */ static struct pam_conv conv = { do_pam_conversation, NULL }; -static pam_handle_t *pamh = NULL; -static const char *pampasswd = NULL; -static char *pam_msg = NULL; -extern ServerOptions options; +static char *__pam_msg = NULL; +static pam_handle_t *__pamh = NULL; +static const char *__pampasswd = NULL; /* states for do_pam_conversation() */ -typedef enum { INITIAL_LOGIN, OTHER } pamstates; -static pamstates pamstate = INITIAL_LOGIN; +enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN; /* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */ static int password_change_required = 0; /* remember whether the last pam_authenticate() succeeded or not */ @@ -69,13 +64,19 @@ static int was_authenticated = 0; static int session_opened = 0; static int creds_set = 0; -/* - * accessor which allows us to switch conversation structs according to - * the authentication method being used - */ +/* accessor which allows us to switch conversation structs according to + * the authentication method being used */ void do_pam_set_conv(struct pam_conv *conv) { - pam_set_item(pamh, PAM_CONV, conv); + pam_set_item(__pamh, PAM_CONV, conv); +} + +/* start an authentication run */ +int do_pam_authenticate(int flags) +{ + int retval = pam_authenticate(__pamh, flags); + was_authenticated = (retval == PAM_SUCCESS); + return retval; } /* @@ -84,10 +85,10 @@ void do_pam_set_conv(struct pam_conv *conv) * * INITIAL_LOGIN mode simply feeds the password from the client into * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output - * messages with pam_msg_cat(). This is used during initial + * messages with into __pam_msg. This is used during initial * authentication to bypass the normal PAM password prompt. * - * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1) + * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase() * and outputs messages to stderr. This mode is used if pam_chauthtok() * is called to update expired passwords. */ @@ -101,43 +102,31 @@ static int do_pam_conversation(int num_msg, const struct pam_message **msg, /* PAM will free this later */ reply = malloc(num_msg * sizeof(*reply)); if (reply == NULL) - return PAM_CONV_ERR; + return PAM_CONV_ERR; for (count = 0; count < num_msg; count++) { - switch ((*msg)[count].msg_style) { + if (pamstate == INITIAL_LOGIN) { + /* + * We can't use stdio yet, queue messages for + * printing later + */ + switch(PAM_MSG_MEMBER(msg, count, msg_style)) { case PAM_PROMPT_ECHO_ON: - if (pamstate == INITIAL_LOGIN) { + free(reply); + return PAM_CONV_ERR; + case PAM_PROMPT_ECHO_OFF: + if (__pampasswd == NULL) { free(reply); return PAM_CONV_ERR; - } else { - fputs((*msg)[count].msg, stderr); - fgets(buf, sizeof(buf), stdin); - reply[count].resp = xstrdup(buf); - reply[count].resp_retcode = PAM_SUCCESS; - break; - } - case PAM_PROMPT_ECHO_OFF: - if (pamstate == INITIAL_LOGIN) { - if (pampasswd == NULL) { - free(reply); - return PAM_CONV_ERR; - } - reply[count].resp = xstrdup(pampasswd); - } else { - reply[count].resp = - xstrdup(read_passphrase((*msg)[count].msg, 1)); } + reply[count].resp = xstrdup(__pampasswd); reply[count].resp_retcode = PAM_SUCCESS; break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: if ((*msg)[count].msg != NULL) { - if (pamstate == INITIAL_LOGIN) - pam_msg_cat((*msg)[count].msg); - else { - fputs((*msg)[count].msg, stderr); - fputs("\n", stderr); - } + message_cat(&__pam_msg, + PAM_MSG_MEMBER(msg, count, msg)); } reply[count].resp = xstrdup(""); reply[count].resp_retcode = PAM_SUCCESS; @@ -145,6 +134,36 @@ static int do_pam_conversation(int num_msg, const struct pam_message **msg, default: free(reply); return PAM_CONV_ERR; + } + } else { + /* + * stdio is connected, so interact directly + */ + switch(PAM_MSG_MEMBER(msg, count, msg_style)) { + case PAM_PROMPT_ECHO_ON: + fputs(PAM_MSG_MEMBER(msg, count, msg), stderr); + fgets(buf, sizeof(buf), stdin); + reply[count].resp = xstrdup(buf); + reply[count].resp_retcode = PAM_SUCCESS; + break; + case PAM_PROMPT_ECHO_OFF: + reply[count].resp = + read_passphrase(PAM_MSG_MEMBER(msg, count, + msg), RP_ALLOW_STDIN); + reply[count].resp_retcode = PAM_SUCCESS; + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + if ((*msg)[count].msg != NULL) + fprintf(stderr, "%s\n", + PAM_MSG_MEMBER(msg, count, msg)); + reply[count].resp = xstrdup(""); + reply[count].resp_retcode = PAM_SUCCESS; + break; + default: + free(reply); + return PAM_CONV_ERR; + } } } @@ -156,61 +175,60 @@ static int do_pam_conversation(int num_msg, const struct pam_message **msg, /* Called at exit to cleanly shutdown PAM */ void do_pam_cleanup_proc(void *context) { - int pam_retval; + int pam_retval = PAM_SUCCESS; - if (pamh != NULL && session_opened) { - pam_retval = pam_close_session(pamh, 0); - if (pam_retval != PAM_SUCCESS) { - log("Cannot close PAM session[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + if (__pamh && session_opened) { + pam_retval = pam_close_session(__pamh, 0); + if (pam_retval != PAM_SUCCESS) + log("Cannot close PAM session[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); } - if (pamh != NULL && creds_set) { - pam_retval = pam_setcred(pamh, PAM_DELETE_CRED); - if (pam_retval != PAM_SUCCESS) { + if (__pamh && creds_set) { + pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED); + if (pam_retval != PAM_SUCCESS) debug("Cannot delete credentials[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + pam_retval, PAM_STRERROR(__pamh, pam_retval)); } - if (pamh != NULL) { - pam_retval = pam_end(pamh, pam_retval); - if (pam_retval != PAM_SUCCESS) { - log("Cannot release PAM authentication[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + if (__pamh) { + pam_retval = pam_end(__pamh, pam_retval); + if (pam_retval != PAM_SUCCESS) + log("Cannot release PAM authentication[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); } } /* Attempt password authentation using PAM */ int auth_pam_password(Authctxt *authctxt, const char *password) { - struct passwd *pw = authctxt->pw; + extern ServerOptions options; int pam_retval; + struct passwd *pw = authctxt->pw; do_pam_set_conv(&conv); /* deny if no user. */ if (pw == NULL) return 0; - if (pw->pw_uid == 0 && options.permit_root_login == 2) + if (pw->pw_uid == 0 && options.permit_root_login == PERMIT_NO_PASSWD) return 0; if (*password == '\0' && options.permit_empty_passwd == 0) return 0; - pampasswd = password; - + __pampasswd = password; + pamstate = INITIAL_LOGIN; - pam_retval = pam_authenticate(pamh, 0); - was_authenticated = (pam_retval == PAM_SUCCESS); + pam_retval = do_pam_authenticate( + options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); if (pam_retval == PAM_SUCCESS) { - debug("PAM Password authentication accepted for user \"%.100s\"", - pw->pw_name); + debug("PAM Password authentication accepted for " + "user \"%.100s\"", pw->pw_name); return 1; } else { - debug("PAM Password authentication for \"%.100s\" failed[%d]: %s", - pw->pw_name, pam_retval, PAM_STRERROR(pamh, pam_retval)); + debug("PAM Password authentication for \"%.100s\" " + "failed[%d]: %s", pw->pw_name, pam_retval, + PAM_STRERROR(__pamh, pam_retval)); return 0; } } @@ -221,41 +239,35 @@ int do_pam_account(char *username, char *remote_user) int pam_retval; do_pam_set_conv(&conv); - - debug("PAM setting rhost to \"%.200s\"", - get_canonical_hostname(options.verify_reverse_mapping)); - pam_retval = pam_set_item(pamh, PAM_RHOST, - get_canonical_hostname(options.verify_reverse_mapping)); - if (pam_retval != PAM_SUCCESS) { - fatal("PAM set rhost failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } - if (remote_user != NULL) { + if (remote_user) { debug("PAM setting ruser to \"%.200s\"", remote_user); - pam_retval = pam_set_item(pamh, PAM_RUSER, remote_user); - if (pam_retval != PAM_SUCCESS) { - fatal("PAM set ruser failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user); + if (pam_retval != PAM_SUCCESS) + fatal("PAM set ruser failed[%d]: %.200s", pam_retval, + PAM_STRERROR(__pamh, pam_retval)); } - pam_retval = pam_acct_mgmt(pamh, 0); + pam_retval = pam_acct_mgmt(__pamh, 0); + debug2("pam_acct_mgmt() = %d", pam_retval); switch (pam_retval) { case PAM_SUCCESS: /* This is what we want */ break; +#if 0 case PAM_NEW_AUTHTOK_REQD: - pam_msg_cat(NEW_AUTHTOK_MSG); + message_cat(&__pam_msg, NEW_AUTHTOK_MSG); /* flag that password change is necessary */ password_change_required = 1; break; +#endif default: - log("PAM rejected by account configuration[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); + log("PAM rejected by account configuration[%d]: " + "%.200s", pam_retval, PAM_STRERROR(__pamh, + pam_retval)); return(0); } - + return(1); } @@ -268,50 +280,51 @@ void do_pam_session(char *username, const char *ttyname) if (ttyname != NULL) { debug("PAM setting tty to \"%.200s\"", ttyname); - pam_retval = pam_set_item(pamh, PAM_TTY, ttyname); - if (pam_retval != PAM_SUCCESS) { - fatal("PAM set tty failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname); + if (pam_retval != PAM_SUCCESS) + fatal("PAM set tty failed[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); } - debug("do_pam_session: euid %u, uid %u", geteuid(), getuid()); - pam_retval = pam_open_session(pamh, 0); - if (pam_retval != PAM_SUCCESS) { - fatal("PAM session setup failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + pam_retval = pam_open_session(__pamh, 0); + if (pam_retval != PAM_SUCCESS) + fatal("PAM session setup failed[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); session_opened = 1; } -/* Set PAM credentials */ -void do_pam_setcred(void) +/* Set PAM credentials */ +void do_pam_setcred(int init) { int pam_retval; + if (__pamh == NULL) + return; + do_pam_set_conv(&conv); - + debug("PAM establishing creds"); - pam_retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); + pam_retval = pam_setcred(__pamh, + init ? PAM_ESTABLISH_CRED : PAM_REINITIALIZE_CRED); if (pam_retval != PAM_SUCCESS) { if (was_authenticated) fatal("PAM setcred failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); + pam_retval, PAM_STRERROR(__pamh, pam_retval)); else debug("PAM setcred failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); + pam_retval, PAM_STRERROR(__pamh, pam_retval)); } else creds_set = 1; } /* accessor function for file scope static variable */ -int pam_password_change_required(void) +int is_pam_password_change_required(void) { return password_change_required; } -/* +/* * Have user change authentication token if pam_acct_mgmt() indicated * it was expired. This needs to be called after an interactive * session is established and the user's pty is connected to @@ -325,16 +338,10 @@ void do_pam_chauthtok(void) if (password_change_required) { pamstate = OTHER; - /* - * XXX: should we really loop forever? - */ - do { - pam_retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); - if (pam_retval != PAM_SUCCESS) { - log("PAM pam_chauthtok failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } - } while (pam_retval != PAM_SUCCESS); + pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + if (pam_retval != PAM_SUCCESS) + fatal("PAM pam_chauthtok failed[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); } } @@ -346,32 +353,40 @@ void finish_pam(void) } /* Start PAM authentication for specified account */ -void start_pam(struct passwd *pw) +void start_pam(const char *user) { int pam_retval; + extern ServerOptions options; + extern u_int utmp_len; + const char *rhost; - debug("Starting up PAM with username \"%.200s\"", pw->pw_name); + debug("Starting up PAM with username \"%.200s\"", user); - pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, &pamh); + pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh); - if (pam_retval != PAM_SUCCESS) { - fatal("PAM initialisation failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + if (pam_retval != PAM_SUCCESS) + fatal("PAM initialisation failed[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); + + rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); + debug("PAM setting rhost to \"%.200s\"", rhost); + pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost); + if (pam_retval != PAM_SUCCESS) + fatal("PAM set rhost failed[%d]: %.200s", pam_retval, + PAM_STRERROR(__pamh, pam_retval)); #ifdef PAM_TTY_KLUDGE /* * Some PAM modules (e.g. pam_time) require a TTY to operate, - * and will fail in various stupid ways if they don't get one. + * and will fail in various stupid ways if they don't get one. * sshd doesn't set the tty until too late in the auth process and may * not even need one (for tty-less connections) - * Kludge: Set a fake PAM_TTY + * Kludge: Set a fake PAM_TTY */ - pam_retval = pam_set_item(pamh, PAM_TTY, "ssh"); - if (pam_retval != PAM_SUCCESS) { - fatal("PAM set tty failed[%d]: %.200s", - pam_retval, PAM_STRERROR(pamh, pam_retval)); - } + pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh"); + if (pam_retval != PAM_SUCCESS) + fatal("PAM set tty failed[%d]: %.200s", + pam_retval, PAM_STRERROR(__pamh, pam_retval)); #endif /* PAM_TTY_KLUDGE */ fatal_add_cleanup(&do_pam_cleanup_proc, NULL); @@ -381,7 +396,7 @@ void start_pam(struct passwd *pw) char **fetch_pam_environment(void) { #ifdef HAVE_PAM_GETENVLIST - return(pam_getenvlist(pamh)); + return(pam_getenvlist(__pamh)); #else /* HAVE_PAM_GETENVLIST */ return(NULL); #endif /* HAVE_PAM_GETENVLIST */ @@ -391,433 +406,29 @@ char **fetch_pam_environment(void) /* or account checking to stderr */ void print_pam_messages(void) { - if (pam_msg != NULL) - fputs(pam_msg, stderr); + if (__pam_msg != NULL) + fputs(__pam_msg, stderr); } -/* Append a message to the PAM message buffer */ -void pam_msg_cat(const char *msg) +/* Append a message to buffer */ +void message_cat(char **p, const char *a) { - char *p; - size_t new_msg_len; - size_t pam_msg_len; - - new_msg_len = strlen(msg); - - if (pam_msg) { - pam_msg_len = strlen(pam_msg); - pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2); - p = pam_msg + pam_msg_len; - } else { - pam_msg = p = xmalloc(new_msg_len + 2); - } - - memcpy(p, msg, new_msg_len); - p[new_msg_len] = '\n'; - p[new_msg_len + 1] = '\0'; -} - -struct inverted_pam_userdata { - /* - * Pipe for telling whether we are doing conversation or sending - * authentication results. - */ - int statefd[2]; - int challengefd[2]; - int responsefd[2]; - - /* Whether we have sent off our challenge */ - int state; -}; + char *cp; + size_t new_len; -#define STATE_CONV 1 -#define STATE_AUTH_OK 2 -#define STATE_AUTH_FAIL 3 + new_len = strlen(a); -int -ssh_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, - void *userdata) { - int i; - FILE *reader; - char buf[1024]; - struct pam_response *reply = NULL; - char state_to_write = STATE_CONV; /* One char to write */ - struct inverted_pam_userdata *ud = userdata; - char *response = NULL; - - /* The stdio functions are more convenient for the read half */ - reader = fdopen(ud->responsefd[0], "rb"); - if (reader == NULL) - goto protocol_failure; - - reply = malloc(num_msg * sizeof(struct pam_response)); - if (reply == NULL) - return PAM_CONV_ERR; - - if (write(ud->statefd[1], &state_to_write, 1) != 1) - goto protocol_failure; - - /* - * Re-package our data and send it off to our better half (the actual SSH - * process) - */ - if (write(ud->challengefd[1], buf, - sprintf(buf, "%d\n", num_msg)) == -1) - goto protocol_failure; - for (i = 0; i < num_msg; i++) { - if (write(ud->challengefd[1], buf, - sprintf(buf, "%d\n", msg[i]->msg_style)) == -1) - goto protocol_failure; - if (write(ud->challengefd[1], buf, - sprintf(buf, "%d\n", strlen(msg[i]->msg))) == -1) - goto protocol_failure; - if (write(ud->challengefd[1], msg[i]->msg, - strlen(msg[i]->msg)) == -1) - goto protocol_failure; - } - /* - * Read back responses. These may not be as nice as we want, as the SSH - * protocol isn't exactly a perfect fit with PAM. - */ - - for (i = 0; i < num_msg; i++) { - char buf[1024]; - char *endptr; - size_t len; /* Length of the response */ - - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - if (fgets(buf, sizeof(buf), reader) == NULL) - goto protocol_failure; - len = (size_t)strtoul(buf, &endptr, 10); - /* The length is supposed to stand on a line by itself */ - if (endptr == NULL || *endptr != '\n') - goto protocol_failure; - response = malloc(len+1); - if (response == NULL) - goto protocol_failure; - if (fread(response, len, 1, reader) != 1) - goto protocol_failure; - response[len] = '\0'; - reply[i].resp = response; - response = NULL; - break; - default: - reply[i].resp = NULL; - break; - } - } - *resp = reply; - return PAM_SUCCESS; - protocol_failure: - free(reply); - return PAM_CONV_ERR; -} - -void -ipam_free_cookie(struct inverted_pam_cookie *cookie) { - struct inverted_pam_userdata *ud; - int i; - - if (cookie == NULL) - return; - ud = cookie->userdata; - cookie->userdata = NULL; - /* Free userdata if allocated */ - if (ud) { - /* Close any opened file descriptors */ - if (ud->statefd[0] != -1) - close(ud->statefd[0]); - if (ud->statefd[1] != -1) - close(ud->statefd[1]); - if (ud->challengefd[0] != -1) - close(ud->challengefd[0]); - if (ud->challengefd[1] != -1) - close(ud->challengefd[1]); - if (ud->responsefd[0] != -1) - close(ud->responsefd[0]); - if (ud->responsefd[1] != -1) - close(ud->responsefd[1]); - free(ud); - ud = NULL; - } - /* Now free the normal cookie */ - if (cookie->pid != 0 && cookie->pid != -1) { - int status; - - /* XXX Use different signal? */ - kill(cookie->pid, SIGKILL); - waitpid(cookie->pid, &status, 0); - } - for (i = 0; i < cookie->num_msg; i++) { - if (cookie->resp && cookie->resp[i]) { - free(cookie->resp[i]->resp); - free(cookie->resp[i]); - } - if (cookie->msg && cookie->msg[i]) { - free((void *)cookie->msg[i]->msg); - free(cookie->msg[i]); - } - } - free(cookie->msg); - free(cookie->resp); - free(cookie); -} - -/* - * Do first half of PAM authentication - this comes to the point where - * you get a message to send to the user. - */ -struct inverted_pam_cookie * -ipam_start_auth(const char *service, const char *username) { - struct inverted_pam_cookie *cookie; - struct inverted_pam_userdata *ud; - static struct pam_conv conv = { - ssh_conv, - NULL - }; - const char *rhost; - - cookie = malloc(sizeof(*cookie)); - if (cookie == NULL) - return NULL; - cookie->state = 0; - /* Set up the cookie so ipam_freecookie can be used on it */ - cookie->num_msg = 0; - cookie->msg = NULL; - cookie->resp = NULL; - cookie->pid = -1; - - ud = calloc(sizeof(*ud), 1); - if (ud == NULL) { - free(cookie); - return NULL; - } - cookie->userdata = ud; - ud->statefd[0] = ud->statefd[1] = -1; - ud->challengefd[0] = ud->challengefd[1] = -1; - ud->responsefd[0] = ud->responsefd[1] = -1; - - if (pipe(ud->statefd) != 0) { - ud->statefd[0] = ud->statefd[1] = -1; - ipam_free_cookie(cookie); - return NULL; - } - if (pipe(ud->challengefd) != 0) { - ud->challengefd[0] = ud->challengefd[1] = -1; - ipam_free_cookie(cookie); - return NULL; - } - if (pipe(ud->responsefd) != 0) { - ud->responsefd[0] = ud->responsefd[1] = -1; - ipam_free_cookie(cookie); - return NULL; - } - rhost = get_canonical_hostname(options.verify_reverse_mapping); - cookie->pid = fork(); - if (cookie->pid == -1) { - ipam_free_cookie(cookie); - return NULL; - } else if (cookie->pid != 0) { - int num_msgs; /* Number of messages from PAM */ - char *endptr; - char buf[1024]; - FILE *reader; - size_t num_msg; - int i; - char state; /* Which state did the connection just enter? */ - - /* We are the parent - wait for a call to the communications - function to turn up, or the challenge to be finished */ - if (read(ud->statefd[0], &state, 1) != 1) { - ipam_free_cookie(cookie); - return NULL; - } - cookie->state = state; - switch (state) { - case STATE_CONV: - /* We are running the conversation function */ - /* The stdio functions are more convenient for read */ - reader = fdopen(ud->challengefd[0], "r"); - if (reader == NULL) { - ipam_free_cookie(cookie); - return NULL; - } - if (fgets(buf, 4, reader) == NULL) { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - num_msg = (size_t)strtoul(buf, &endptr, 10); - /* The length is supposed to stand on a line by itself */ - if (endptr == NULL || *endptr != '\n') { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - cookie->msg = - malloc(sizeof(struct pam_message *) * num_msg); - cookie->resp = - malloc(sizeof(struct pam_response *) * num_msg); - if (cookie->msg == NULL || cookie->resp == NULL) { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - for (i = 0; i < num_msg; i++) { - cookie->msg[i] = - malloc(sizeof(struct pam_message)); - cookie->resp[i] = - malloc(sizeof(struct pam_response)); - if (cookie->msg[i] == NULL || - cookie->resp[i] == NULL) { - for (;;) { - free(cookie->msg[i]); - free(cookie->resp[i]); - if (i == 0) - break; - i--; - } - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - cookie->msg[i]->msg = NULL; - cookie->resp[i]->resp = NULL; - cookie->resp[i]->resp_retcode = 0; - } - /* Set up so the above will be freed on failure */ - cookie->num_msg = num_msg; - /* - * We have a an allocated response and message for - * each of the entries in the PAM structure - transfer - * the data sent to the conversation function over. - */ - for (i = 0; i < num_msg; i++) { - size_t len; - - if (fgets(buf, sizeof(buf), reader) == NULL) { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - cookie->msg[i]->msg_style = - (size_t)strtoul(buf, &endptr, 10); - if (endptr == NULL || *endptr != '\n') { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - if (fgets(buf, sizeof(buf), reader) == NULL) { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - len = (size_t)strtoul(buf, &endptr, 10); - if (endptr == NULL || *endptr != '\n') { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - cookie->msg[i]->msg = malloc(len + 1); - if (cookie->msg[i]->msg == NULL) { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - if (fread((char *)cookie->msg[i]->msg, len, 1, reader) != - 1) { - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - *(char *)&(cookie->msg[i]->msg[len]) = '\0'; - } - break; - case STATE_AUTH_OK: - case STATE_AUTH_FAIL: - break; - default: - /* Internal failure, somehow */ - fclose(reader); - ipam_free_cookie(cookie); - return NULL; - } - return cookie; - } else { - /* We are the child */ - pam_handle_t *pamh=NULL; - int retval; - char state; - - conv.appdata_ptr = ud; - retval = pam_start(service, username, &conv, &pamh); - fprintf(stderr, "pam_start returned %d\n", retval); - if (retval == PAM_SUCCESS) - retval = pam_set_item(pamh, PAM_RHOST, rhost); - /* Is user really user? */ - if (retval == PAM_SUCCESS) - retval = pam_authenticate(pamh, 0); - /* permitted access? */ - if (retval == PAM_SUCCESS) - retval = pam_acct_mgmt(pamh, 0); - /* This is where we have been authorized or not. */ - - /* Be conservative - flag as auth failure if we can't close */ - /* - * XXX This is based on example code from Linux-PAM - - * but can it really be correct to pam_end if - * pam_start failed? - */ - if (pam_end(pamh, retval) != PAM_SUCCESS) - retval = PAM_AUTH_ERR; - - /* Message to parent */ - state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL; - if (write(ud->statefd[1], &state, 1) != 1) { - _exit(1); - } - /* FDs will be closed, so further communication will stop */ - _exit(0); - } -} + if (*p) { + size_t len = strlen(*p); -/* - * Do second half of PAM authentication - cookie should now be filled - * in with the response to the challenge. - */ + *p = xrealloc(*p, new_len + len + 2); + cp = *p + len; + } else + *p = cp = xmalloc(new_len + 2); -int -ipam_complete_auth(struct inverted_pam_cookie *cookie) { - int i; - char buf[1024]; - struct inverted_pam_userdata *ud = cookie->userdata; - char state; - - /* Send over our responses */ - for (i = 0; i < cookie->num_msg; i++) { - if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON && - cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) - continue; - if (write(ud->responsefd[1], buf, - sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) { - ipam_free_cookie(cookie); - return 0; - } - if (write(ud->responsefd[1], cookie->resp[i]->resp, - strlen(cookie->resp[i]->resp)) == -1) { - ipam_free_cookie(cookie); - return 0; - } - } - /* Find out what state we are changing to */ - if (read(ud->statefd[0], &state, 1) != 1) { - ipam_free_cookie(cookie); - return 0; - } - - return state == STATE_AUTH_OK ? 1 : 0; + memcpy(cp, a, new_len); + cp[new_len] = '\n'; + cp[new_len + 1] = '\0'; } #endif /* USE_PAM */ |