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"
extern ServerOptions options;
extern Buffer loginmsg;
-
-#define __unused
+extern int compat20;
#ifdef USE_POSIX_THREADS
#include <pthread.h>
*/
typedef pthread_t sp_pthread_t;
#else
+typedef pid_t sp_pthread_t;
+#endif
+
+struct pam_ctxt {
+ sp_pthread_t pam_thread;
+ int pam_psock;
+ int pam_csock;
+ int pam_done;
+};
+
+static void sshpam_free_ctx(void *);
+static struct pam_ctxt *cleanup_ctxt;
+
+#ifndef USE_POSIX_THREADS
/*
* Simulate threads with processes.
*/
-typedef pid_t sp_pthread_t;
+
+static int sshpam_thread_status = -1;
+static mysig_t sshpam_oldsig;
+
+static void
+sshpam_sigchld_handler(int sig)
+{
+ if (cleanup_ctxt == NULL)
+ return; /* handler called after PAM cleanup, shouldn't happen */
+ if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) == -1)
+ return; /* couldn't wait for process */
+ if (WIFSIGNALED(sshpam_thread_status) &&
+ WTERMSIG(sshpam_thread_status) == SIGTERM)
+ return; /* terminated by pthread_cancel */
+ if (!WIFEXITED(sshpam_thread_status))
+ fatal("PAM: authentication thread exited unexpectedly");
+ if (WEXITSTATUS(sshpam_thread_status) != 0)
+ fatal("PAM: authentication thread exited uncleanly");
+}
static void
pthread_exit(void *value __unused)
_exit(1);
default:
*thread = pid;
+ sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
return (0);
}
}
static int
pthread_cancel(sp_pthread_t thread)
{
+ signal(SIGCHLD, sshpam_oldsig);
return (kill(thread, SIGTERM));
}
{
int status;
+ if (sshpam_thread_status != -1)
+ return (sshpam_thread_status);
+ signal(SIGCHLD, sshpam_oldsig);
waitpid(thread, &status, 0);
return (status);
}
static pam_handle_t *sshpam_handle = NULL;
static int sshpam_err = 0;
static int sshpam_authenticated = 0;
-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;
- int pam_psock;
- int pam_csock;
- int pam_done;
-};
-
-static void sshpam_free_ctx(void *);
-static struct pam_ctxt *cleanup_ctxt;
+static int *force_pwchange;
/* Some PAM implementations don't implement this */
#ifndef HAVE_PAM_GETENVLIST
}
#endif
+void
+pam_password_change_required(int reqd)
+{
+ debug3("%s %d", __func__, reqd);
+ *force_pwchange = 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)
u_int i, num_env;
int err;
+ debug3("PAM: %s entering", __func__);
+
+ /* 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));
struct pam_response *reply;
int i;
+ debug3("PAM: %s entering, %d messages", __func__, n);
*resp = NULL;
ctxt = data;
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 (*force_pwchange) {
+ 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, *force_pwchange);
+
/* Export any environment strings set in child */
for(i = 0; environ[i] != NULL; i++)
; /* Count */
{
struct pam_ctxt *ctxt = cleanup_ctxt;
+ debug3("PAM: %s entering", __func__);
if (ctxt != NULL && ctxt->pam_thread != 0) {
pthread_cancel(ctxt->pam_thread);
pthread_join(ctxt->pam_thread, NULL);
sshpam_null_conv(int n, const struct pam_message **msg,
struct pam_response **resp, void *data)
{
+ debug3("PAM: %s entering, %d messages", __func__, n);
return (PAM_CONV_ERR);
}
pam_close_session(sshpam_handle, PAM_SILENT);
sshpam_session_open = 0;
}
- sshpam_authenticated = sshpam_new_authtok_reqd = 0;
+ sshpam_authenticated = 0;
pam_end(sshpam_handle, sshpam_err);
sshpam_handle = NULL;
}
struct pam_ctxt *ctxt;
int socks[2];
+ debug3("PAM: %s entering", __func__);
/* Refuse to start if we don't have PAM enabled */
if (!options.use_pam)
return NULL;
ctxt = xmalloc(sizeof *ctxt);
memset(ctxt, 0, sizeof(*ctxt));
+ force_pwchange = &(authctxt->force_pwchange);
+
/* Start the authentication thread */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
error("PAM: failed create sockets: %s", strerror(errno));
char *msg;
size_t len;
+ debug3("PAM: %s entering", __func__);
buffer_init(&buffer);
*name = xstrdup("");
*info = xstrdup("");
Buffer buffer;
struct pam_ctxt *ctxt = ctx;
- debug2("PAM: %s", __func__);
+ debug2("PAM: %s entering, %d responses", __func__, num);
switch (ctxt->pam_done) {
case 1:
sshpam_authenticated = 1;
{
struct pam_ctxt *ctxt = ctxtp;
+ debug3("PAM: %s entering", __func__);
sshpam_thread_cleanup();
xfree(ctxt);
/*
u_int
do_pam_account(void)
{
- 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)
- return (0);
+ if (sshpam_account_status != -1)
+ return (sshpam_account_status);
- 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;
+ sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
+ debug3("PAM: %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);
}
- return (1);
+ if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
+ pam_password_change_required(1);
+
+ sshpam_account_status = 1;
+ return (sshpam_account_status);
}
void
pam_strerror(sshpam_handle, sshpam_err));
}
-int
-is_pam_password_change_required(void)
-{
- return (sshpam_new_authtok_reqd);
-}
-
static int
pam_tty_conv(int n, const struct pam_message **msg,
struct pam_response **resp, void *data)
struct pam_response *reply;
int i;
+ debug3("PAM: %s called with %d messages", __func__, n);
+
*resp = NULL;
if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
pam_strerror(sshpam_handle, sshpam_err));
}
+static int
+pam_store_conv(int n, const struct pam_message **msg,
+ struct pam_response **resp, void *data)
+{
+ struct pam_response *reply;
+ int i;
+ size_t len;
+
+ debug3("PAM: %s called with %d messages", __func__, n);
+ *resp = NULL;
+
+ if (n <= 0 || n > PAM_MAX_NUM_MSG)
+ return (PAM_CONV_ERR);
+
+ if ((reply = malloc(n * sizeof(*reply))) == NULL)
+ return (PAM_CONV_ERR);
+ memset(reply, 0, n * sizeof(*reply));
+
+ for (i = 0; i < n; ++i) {
+ switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ len = strlen(PAM_MSG_MEMBER(msg, i, msg));
+ buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
+ buffer_append(&loginmsg, "\n", 1 );
+ reply[i].resp_retcode = PAM_SUCCESS;
+ break;
+ default:
+ goto fail;
+ }
+ }
+ *resp = reply;
+ return (PAM_SUCCESS);
+
+ fail:
+ for(i = 0; i < n; i++) {
+ if (reply[i].resp != NULL)
+ xfree(reply[i].resp);
+ }
+ xfree(reply);
+ return (PAM_CONV_ERR);
+}
+
+static struct pam_conv store_conv = { pam_store_conv, NULL };
+
void
do_pam_session(void)
{
+ debug3("PAM: opening session");
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
- (const void *)&tty_conv);
+ (const void *)&store_conv);
if (sshpam_err != PAM_SUCCESS)
fatal("PAM: failed to set PAM_CONV: %s",
pam_strerror(sshpam_handle, sshpam_err));
return (ret);
}
-void
-print_pam_messages(void)
-{
- /* XXX */
-}
-
char **
fetch_pam_child_environment(void)
{