RCSID("$Id$");
#ifdef USE_PAM
-#if defined(HAVE_SECURITY_PAM_APPL_H)
#include <security/pam_appl.h>
-#elif defined (HAVE_PAM_PAM_APPL_H)
-#include <pam/pam_appl.h>
-#endif
#include "auth.h"
#include "auth-pam.h"
#include "auth-options.h"
extern ServerOptions options;
-extern Buffer loginmsg;
-extern int compat20;
#define __unused
#ifdef USE_POSIX_THREADS
#include <pthread.h>
/*
- * Avoid namespace clash when *not* using pthreads for systems *with*
- * pthreads, which unconditionally define pthread_t via sys/types.h
+ * Avoid namespace clash when *not* using pthreads for systems *with*
+ * pthreads, which unconditionally define pthread_t via sys/types.h
* (e.g. Linux)
*/
-typedef pthread_t sp_pthread_t;
+typedef pthread_t sp_pthread_t;
#else
/*
* Simulate threads with processes.
static int sshpam_new_authtok_reqd = 0;
static int sshpam_session_open = 0;
static int sshpam_cred_established = 0;
-static int sshpam_account_status = -1;
-static char **sshpam_env = NULL;
struct pam_ctxt {
sp_pthread_t pam_thread;
};
static void sshpam_free_ctx(void *);
-static struct pam_ctxt *cleanup_ctxt;
-
-/* Some PAM implementations don't implement this */
-#ifndef HAVE_PAM_GETENVLIST
-static char **
-pam_getenvlist(pam_handle_t *pamh)
-{
- /*
- * XXX - If necessary, we can still support envrionment passing
- * for platforms without pam_getenvlist by searching for known
- * env vars (e.g. KRB5CCNAME) from the PAM environment.
- */
- return NULL;
-}
-#endif
-
-void
-pam_password_change_required(int reqd)
-{
- sshpam_new_authtok_reqd = reqd;
- if (reqd) {
- no_port_forwarding_flag |= 2;
- no_agent_forwarding_flag |= 2;
- no_x11_forwarding_flag |= 2;
- } else {
- no_port_forwarding_flag &= ~2;
- no_agent_forwarding_flag &= ~2;
- no_x11_forwarding_flag &= ~2;
-
- }
-}
-/* Import regular and PAM environment from subprocess */
-static void
-import_environments(Buffer *b)
-{
- char *env;
- u_int i, num_env;
- int err;
-
- /* Import variables set by do_pam_account */
- sshpam_account_status = buffer_get_int(b);
- pam_password_change_required(buffer_get_int(b));
-
- /* Import environment from subprocess */
- num_env = buffer_get_int(b);
- sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));
- debug3("PAM: num env strings %d", num_env);
- for(i = 0; i < num_env; i++)
- sshpam_env[i] = buffer_get_string(b, NULL);
-
- sshpam_env[num_env] = NULL;
-
- /* Import PAM environment from subprocess */
- num_env = buffer_get_int(b);
- debug("PAM: num PAM env strings %d", num_env);
- for(i = 0; i < num_env; i++) {
- env = buffer_get_string(b, NULL);
-
-#ifdef HAVE_PAM_PUTENV
- /* Errors are not fatal here */
- if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
- error("PAM: pam_putenv: %s",
- pam_strerror(sshpam_handle, sshpam_err));
- }
-#endif
- }
-}
/*
* Conversation function for authentication thread.
for (i = 0; i < n; ++i) {
switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
case PAM_PROMPT_ECHO_OFF:
- buffer_put_cstring(&buffer,
+ buffer_put_cstring(&buffer,
PAM_MSG_MEMBER(msg, i, msg));
- if (ssh_msg_send(ctxt->pam_csock,
- PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
- goto fail;
- if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
- goto fail;
+ ssh_msg_send(ctxt->pam_csock,
+ PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
+ ssh_msg_recv(ctxt->pam_csock, &buffer);
if (buffer_get_char(&buffer) != PAM_AUTHTOK)
goto fail;
reply[i].resp = buffer_get_string(&buffer, NULL);
break;
case PAM_PROMPT_ECHO_ON:
- buffer_put_cstring(&buffer,
+ buffer_put_cstring(&buffer,
PAM_MSG_MEMBER(msg, i, msg));
- if (ssh_msg_send(ctxt->pam_csock,
- PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
- goto fail;
- if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
- goto fail;
+ ssh_msg_send(ctxt->pam_csock,
+ PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
+ ssh_msg_recv(ctxt->pam_csock, &buffer);
if (buffer_get_char(&buffer) != PAM_AUTHTOK)
goto fail;
reply[i].resp = buffer_get_string(&buffer, NULL);
break;
case PAM_ERROR_MSG:
- buffer_put_cstring(&buffer,
+ buffer_put_cstring(&buffer,
PAM_MSG_MEMBER(msg, i, msg));
- if (ssh_msg_send(ctxt->pam_csock,
- PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
- goto fail;
+ ssh_msg_send(ctxt->pam_csock,
+ PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
break;
case PAM_TEXT_INFO:
- buffer_put_cstring(&buffer,
+ buffer_put_cstring(&buffer,
PAM_MSG_MEMBER(msg, i, msg));
- if (ssh_msg_send(ctxt->pam_csock,
- PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
- goto fail;
+ ssh_msg_send(ctxt->pam_csock,
+ PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
break;
default:
goto fail;
Buffer buffer;
struct pam_conv sshpam_conv;
#ifndef USE_POSIX_THREADS
- extern char **environ;
- char **env_from_pam;
- u_int i;
const char *pam_user;
pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
setproctitle("%s [pam]", pam_user);
- environ[0] = NULL;
#endif
sshpam_conv.conv = sshpam_thread_conv;
sshpam_err = pam_authenticate(sshpam_handle, 0);
if (sshpam_err != PAM_SUCCESS)
goto auth_fail;
-
- if (compat20) {
- if (!do_pam_account())
- goto auth_fail;
- if (sshpam_new_authtok_reqd) {
- sshpam_err = pam_chauthtok(sshpam_handle,
- PAM_CHANGE_EXPIRED_AUTHTOK);
- if (sshpam_err != PAM_SUCCESS)
- goto auth_fail;
- pam_password_change_required(0);
- }
- }
-
buffer_put_cstring(&buffer, "OK");
-
-#ifndef USE_POSIX_THREADS
- /* Export variables set by do_pam_account */
- buffer_put_int(&buffer, sshpam_account_status);
- buffer_put_int(&buffer, sshpam_new_authtok_reqd);
-
- /* Export any environment strings set in child */
- for(i = 0; environ[i] != NULL; i++)
- ; /* Count */
- buffer_put_int(&buffer, i);
- for(i = 0; environ[i] != NULL; i++)
- buffer_put_cstring(&buffer, environ[i]);
-
- /* Export any environment strings set by PAM in child */
- env_from_pam = pam_getenvlist(sshpam_handle);
- for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
- ; /* Count */
- buffer_put_int(&buffer, i);
- for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
- buffer_put_cstring(&buffer, env_from_pam[i]);
-#endif /* USE_POSIX_THREADS */
-
- /* XXX - can't do much about an error here */
ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
buffer_free(&buffer);
pthread_exit(NULL);
auth_fail:
buffer_put_cstring(&buffer,
pam_strerror(sshpam_handle, sshpam_err));
- /* XXX - can't do much about an error here */
ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
buffer_free(&buffer);
pthread_exit(NULL);
-
+
return (NULL); /* Avoid warning for non-pthread case */
}
-void
-sshpam_thread_cleanup(void)
+static void
+sshpam_thread_cleanup(void *ctxtp)
{
- struct pam_ctxt *ctxt = cleanup_ctxt;
-
- if (ctxt != NULL && ctxt->pam_thread != 0) {
- pthread_cancel(ctxt->pam_thread);
- pthread_join(ctxt->pam_thread, NULL);
- close(ctxt->pam_psock);
- close(ctxt->pam_csock);
- memset(ctxt, 0, sizeof(*ctxt));
- cleanup_ctxt = NULL;
- }
+ struct pam_ctxt *ctxt = ctxtp;
+
+ pthread_cancel(ctxt->pam_thread);
+ pthread_join(ctxt->pam_thread, NULL);
+ close(ctxt->pam_psock);
+ close(ctxt->pam_csock);
}
static int
static struct pam_conv null_conv = { sshpam_null_conv, NULL };
-void
-sshpam_cleanup(void)
+static void
+sshpam_cleanup(void *arg)
{
+ (void)arg;
debug("PAM: cleanup");
if (sshpam_handle == NULL)
return;
PAM_USER, (const void **)&pam_user);
if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
return (0);
+ fatal_remove_cleanup(sshpam_cleanup, NULL);
pam_end(sshpam_handle, sshpam_err);
sshpam_handle = NULL;
}
return (-1);
}
#ifdef PAM_TTY_KLUDGE
- /*
- * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
- * sshd doesn't set the tty until too late in the auth process and
+ /*
+ * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
+ * sshd doesn't set the tty until too late in the auth process and
* may not even set one (for tty-less connections)
- */
+ */
debug("PAM: setting PAM_TTY to \"ssh\"");
sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
if (sshpam_err != PAM_SUCCESS) {
return (-1);
}
#endif
+ fatal_add_cleanup(sshpam_cleanup, NULL);
return (0);
}
}
ctxt = xmalloc(sizeof *ctxt);
- memset(ctxt, 0, sizeof(*ctxt));
+ ctxt->pam_done = 0;
/* Start the authentication thread */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
xfree(ctxt);
return (NULL);
}
- cleanup_ctxt = ctxt;
+ fatal_add_cleanup(sshpam_thread_cleanup, ctxt);
return (ctxt);
}
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
/* accumulate messages */
- len = plen + strlen(msg) + 2;
+ len = plen + strlen(msg) + 1;
**prompts = xrealloc(**prompts, len);
- plen += snprintf(**prompts + plen, len, "%s\n", msg);
+ plen += snprintf(**prompts + plen, len, "%s", msg);
xfree(msg);
break;
case PAM_SUCCESS:
case PAM_AUTH_ERR:
if (**prompts != NULL) {
/* drain any accumulated messages */
- debug("PAM: %s", **prompts);
- buffer_append(&loginmsg, **prompts,
- strlen(**prompts));
+#if 0 /* XXX - not compatible with privsep */
+ packet_start(SSH2_MSG_USERAUTH_BANNER);
+ packet_put_cstring(**prompts);
+ packet_put_cstring("");
+ packet_send();
+ packet_write_wait();
+#endif
xfree(**prompts);
**prompts = NULL;
}
if (type == PAM_SUCCESS) {
- import_environments(&buffer);
*num = 0;
**echo_on = 0;
ctxt->pam_done = 1;
return (0);
}
error("PAM: %s", msg);
- /* FALLTHROUGH */
default:
*num = 0;
**echo_on = 0;
}
buffer_init(&buffer);
buffer_put_cstring(&buffer, *resp);
- if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
- buffer_free(&buffer);
- return (-1);
- }
+ ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer);
buffer_free(&buffer);
return (1);
}
{
struct pam_ctxt *ctxt = ctxtp;
- sshpam_thread_cleanup();
+ fatal_remove_cleanup(sshpam_thread_cleanup, ctxt);
+ sshpam_thread_cleanup(ctxtp);
xfree(ctxt);
/*
* We don't call sshpam_cleanup() here because we may need the PAM
void
finish_pam(void)
{
- sshpam_cleanup();
+ fatal_remove_cleanup(sshpam_cleanup, NULL);
+ sshpam_cleanup(NULL);
}
u_int
do_pam_account(void)
{
- if (sshpam_account_status != -1)
- return (sshpam_account_status);
-
sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
debug3("%s: pam_acct_mgmt = %d", __func__, sshpam_err);
- if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
- sshpam_account_status = 0;
- return (sshpam_account_status);
+ if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD)
+ return (0);
+
+ if (sshpam_err == PAM_NEW_AUTHTOK_REQD) {
+ sshpam_new_authtok_reqd = 1;
+
+ /* Prevent forwardings until password changed */
+ no_port_forwarding_flag |= 2;
+ no_agent_forwarding_flag |= 2;
+ no_x11_forwarding_flag |= 2;
}
- if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
- pam_password_change_required(1);
+ return (1);
+}
- sshpam_account_status = 1;
- return (sshpam_account_status);
+void
+do_pam_session(void)
+{
+ sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
+ (const void *)&null_conv);
+ if (sshpam_err != PAM_SUCCESS)
+ fatal("PAM: failed to set PAM_CONV: %s",
+ pam_strerror(sshpam_handle, sshpam_err));
+ sshpam_err = pam_open_session(sshpam_handle, 0);
+ if (sshpam_err != PAM_SUCCESS)
+ fatal("PAM: pam_open_session(): %s",
+ pam_strerror(sshpam_handle, sshpam_err));
+ sshpam_session_open = 1;
}
void
}
static int
-pam_tty_conv(int n, const struct pam_message **msg,
+pam_chauthtok_conv(int n, const struct pam_message **msg,
struct pam_response **resp, void *data)
{
char input[PAM_MAX_MSG_SIZE];
*resp = NULL;
- if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
+ if (n <= 0 || n > PAM_MAX_NUM_MSG)
return (PAM_CONV_ERR);
if ((reply = malloc(n * sizeof(*reply))) == NULL)
switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
case PAM_PROMPT_ECHO_OFF:
reply[i].resp =
- read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
+ read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
RP_ALLOW_STDIN);
reply[i].resp_retcode = PAM_SUCCESS;
break;
case PAM_PROMPT_ECHO_ON:
- fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
+ fputs(PAM_MSG_MEMBER(msg, i, msg), stderr);
fgets(input, sizeof input, stdin);
reply[i].resp = xstrdup(input);
reply[i].resp_retcode = PAM_SUCCESS;
break;
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
- fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
+ fputs(PAM_MSG_MEMBER(msg, i, msg), stderr);
reply[i].resp_retcode = PAM_SUCCESS;
break;
default:
return (PAM_CONV_ERR);
}
-static struct pam_conv tty_conv = { pam_tty_conv, NULL };
-
/*
* XXX this should be done in the authentication phase, but ssh1 doesn't
* support that
void
do_pam_chauthtok(void)
{
+ struct pam_conv pam_conv;
+
+ pam_conv.conv = pam_chauthtok_conv;
+ pam_conv.appdata_ptr = NULL;
+
if (use_privsep)
fatal("Password expired (unable to change with privsep)");
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
- (const void *)&tty_conv);
+ (const void *)&pam_conv);
if (sshpam_err != PAM_SUCCESS)
fatal("PAM: failed to set PAM_CONV: %s",
pam_strerror(sshpam_handle, sshpam_err));
pam_strerror(sshpam_handle, sshpam_err));
}
-void
-do_pam_session(void)
-{
- sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
- (const void *)&tty_conv);
- if (sshpam_err != PAM_SUCCESS)
- fatal("PAM: failed to set PAM_CONV: %s",
- pam_strerror(sshpam_handle, sshpam_err));
- sshpam_err = pam_open_session(sshpam_handle, 0);
- if (sshpam_err != PAM_SUCCESS)
- fatal("PAM: pam_open_session(): %s",
- pam_strerror(sshpam_handle, sshpam_err));
- sshpam_session_open = 1;
-}
-
-/*
+/*
* Set a PAM environment string. We need to do this so that the session
* modules can handle things like Kerberos/GSI credentials that appear
* during the ssh authentication process.
*/
+
int
-do_pam_putenv(char *name, char *value)
+do_pam_putenv(char *name, char *value)
{
int ret = 1;
-#ifdef HAVE_PAM_PUTENV
+#ifdef HAVE_PAM_PUTENV
char *compound;
size_t len;
/* XXX */
}
-char **
-fetch_pam_child_environment(void)
-{
- return sshpam_env;
-}
-
char **
fetch_pam_environment(void)
{
+#ifdef HAVE_PAM_GETENVLIST
+ debug("PAM: retrieving environment");
return (pam_getenvlist(sshpam_handle));
+#else
+ return (NULL);
+#endif
}
void