summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2002-12-14 13:52:39 +0000
committerdes <des@FreeBSD.org>2002-12-14 13:52:39 +0000
commit06b0ce4f651a97f9ac03d4ba5be051af78b485a7 (patch)
tree151177d747202a0dea48317bf08a5027dcfa7067 /crypto
parentb6985eb271ae3a1dc1a45d1bfe3c3e9a56b283fb (diff)
downloadFreeBSD-src-06b0ce4f651a97f9ac03d4ba5be051af78b485a7.zip
FreeBSD-src-06b0ce4f651a97f9ac03d4ba5be051af78b485a7.tar.gz
If possible, use pthreads instead of a child process for PAM.
Reimplement the necessary bits from auth_pam.c and auth2_pam.c so that they share the PAM context used by the keyboard-interactive thread. If a child process is used instead, they will (necessarily) use a separate context. Constify do_pam_account() and do_pam_session(). Sponsored by: DARPA, NAI Labs
Diffstat (limited to 'crypto')
-rw-r--r--crypto/openssh/auth-pam.h4
-rw-r--r--crypto/openssh/auth2-pam-freebsd.c395
2 files changed, 334 insertions, 65 deletions
diff --git a/crypto/openssh/auth-pam.h b/crypto/openssh/auth-pam.h
index 05a3ba0..376d9ea 100644
--- a/crypto/openssh/auth-pam.h
+++ b/crypto/openssh/auth-pam.h
@@ -38,8 +38,8 @@ int auth_pam_password(Authctxt *authctxt, const char *password);
char **fetch_pam_environment(void);
void free_pam_environment(char **env);
int do_pam_authenticate(int flags);
-int do_pam_account(char *username, char *remote_user);
-void do_pam_session(char *username, const char *ttyname);
+int do_pam_account(const char *username, const char *remote_user);
+void do_pam_session(const char *username, const char *ttyname);
void do_pam_setcred(int init);
void print_pam_messages(void);
int is_pam_password_change_required(void);
diff --git a/crypto/openssh/auth2-pam-freebsd.c b/crypto/openssh/auth2-pam-freebsd.c
index 4ea9036..895ce5a 100644
--- a/crypto/openssh/auth2-pam-freebsd.c
+++ b/crypto/openssh/auth2-pam-freebsd.c
@@ -36,29 +36,90 @@ RCSID("$FreeBSD$");
#include <security/pam_appl.h>
#include "auth.h"
+#include "auth-pam.h"
#include "buffer.h"
#include "bufaux.h"
+#include "canohost.h"
#include "log.h"
#include "monitor_wrap.h"
#include "msg.h"
#include "packet.h"
+#include "readpass.h"
+#include "servconf.h"
#include "ssh2.h"
#include "xmalloc.h"
+#ifdef USE_POSIX_THREADS
+#include <pthread.h>
+#else
+/*
+ * Simulate threads with processes.
+ */
+typedef pid_t pthread_t;
+
+static void
+pthread_exit(void *value __unused)
+{
+ _exit(0);
+}
+
+static int
+pthread_create(pthread_t *thread, const void *attr __unused,
+ void *(*thread_start)(void *), void *arg)
+{
+ pid_t pid;
+
+ switch ((pid = fork())) {
+ case -1:
+ error("fork(): %s", strerror(errno));
+ return (-1);
+ case 0:
+ thread_start(arg);
+ _exit(1);
+ default:
+ *thread = pid;
+ return (0);
+ }
+}
+
+static int
+pthread_cancel(pthread_t thread)
+{
+ return (kill(thread, SIGTERM));
+}
+
+static int
+pthread_join(pthread_t thread, void **value __unused)
+{
+ int status;
+
+ waitpid(thread, &status, 0);
+ return (status);
+}
+#endif
+
+
+static pam_handle_t *pam_handle;
+static int pam_err;
+static int pam_authenticated;
+static int pam_new_authtok_reqd;
+static int pam_session_open;
+static int pam_cred_established;
+
struct pam_ctxt {
- char *pam_user;
- pid_t pam_pid;
- int pam_sock;
+ pthread_t pam_thread;
+ int pam_psock;
+ int pam_csock;
int pam_done;
};
static void pam_free_ctx(void *);
/*
- * Conversation function for child process.
+ * Conversation function for authentication thread.
*/
static int
-pam_child_conv(int n,
+pam_thread_conv(int n,
const struct pam_message **msg,
struct pam_response **resp,
void *data)
@@ -78,27 +139,27 @@ pam_child_conv(int n,
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
buffer_put_cstring(&buffer, msg[i]->msg);
- ssh_msg_send(ctxt->pam_sock, msg[i]->msg_style, &buffer);
- ssh_msg_recv(ctxt->pam_sock, &buffer);
+ ssh_msg_send(ctxt->pam_csock, msg[i]->msg_style, &buffer);
+ ssh_msg_recv(ctxt->pam_csock, &buffer);
if (buffer_get_char(&buffer) != PAM_AUTHTOK)
goto fail;
resp[i]->resp = buffer_get_string(&buffer, NULL);
break;
case PAM_PROMPT_ECHO_ON:
buffer_put_cstring(&buffer, msg[i]->msg);
- ssh_msg_send(ctxt->pam_sock, msg[i]->msg_style, &buffer);
- ssh_msg_recv(ctxt->pam_sock, &buffer);
+ ssh_msg_send(ctxt->pam_csock, msg[i]->msg_style, &buffer);
+ ssh_msg_recv(ctxt->pam_csock, &buffer);
if (buffer_get_char(&buffer) != PAM_AUTHTOK)
goto fail;
resp[i]->resp = buffer_get_string(&buffer, NULL);
break;
case PAM_ERROR_MSG:
buffer_put_cstring(&buffer, msg[i]->msg);
- ssh_msg_send(ctxt->pam_sock, msg[i]->msg_style, &buffer);
+ ssh_msg_send(ctxt->pam_csock, msg[i]->msg_style, &buffer);
break;
case PAM_TEXT_INFO:
buffer_put_cstring(&buffer, msg[i]->msg);
- ssh_msg_send(ctxt->pam_sock, msg[i]->msg_style, &buffer);
+ ssh_msg_send(ctxt->pam_csock, msg[i]->msg_style, &buffer);
break;
default:
goto fail;
@@ -117,49 +178,99 @@ pam_child_conv(int n,
}
/*
- * Child process.
+ * Authentication thread.
*/
static void *
-pam_child(struct pam_ctxt *ctxt)
+pam_thread(void *ctxtp)
{
+ struct pam_ctxt *ctxt = ctxtp;
Buffer buffer;
- struct pam_conv pam_conv = { pam_child_conv, ctxt };
- pam_handle_t *pamh;
- int pam_err;
+ struct pam_conv pam_conv = { pam_thread_conv, ctxt };
buffer_init(&buffer);
- setproctitle("%s [pam]", ctxt->pam_user);
- pam_err = pam_start("sshd", ctxt->pam_user, &pam_conv, &pamh);
+ pam_err = pam_set_item(pam_handle,
+ PAM_CONV, (const void *)&pam_conv);
if (pam_err != PAM_SUCCESS)
goto auth_fail;
- pam_err = pam_authenticate(pamh, 0);
+ pam_err = pam_authenticate(pam_handle, 0);
if (pam_err != PAM_SUCCESS)
goto auth_fail;
- pam_err = pam_acct_mgmt(pamh, 0);
+ pam_err = pam_acct_mgmt(pam_handle, 0);
if (pam_err != PAM_SUCCESS)
goto auth_fail;
buffer_put_cstring(&buffer, "OK");
- ssh_msg_send(ctxt->pam_sock, PAM_SUCCESS, &buffer);
+ ssh_msg_send(ctxt->pam_csock, PAM_SUCCESS, &buffer);
buffer_free(&buffer);
- pam_end(pamh, pam_err);
- exit(0);
+ pthread_exit(NULL);
auth_fail:
- buffer_put_cstring(&buffer, pam_strerror(pamh, pam_err));
- ssh_msg_send(ctxt->pam_sock, PAM_AUTH_ERR, &buffer);
+ buffer_put_cstring(&buffer,
+ pam_strerror(pam_handle, pam_err));
+ ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
buffer_free(&buffer);
- pam_end(pamh, pam_err);
- exit(0);
+ pthread_exit(NULL);
}
static void
-pam_cleanup(void *ctxtp)
+pam_thread_cleanup(void *ctxtp)
{
struct pam_ctxt *ctxt = ctxtp;
- int status;
- close(ctxt->pam_sock);
- kill(ctxt->pam_pid, SIGHUP);
- waitpid(ctxt->pam_pid, &status, 0);
+ pthread_cancel(ctxt->pam_thread);
+ pthread_join(ctxt->pam_thread, NULL);
+ close(ctxt->pam_psock);
+ close(ctxt->pam_csock);
+}
+
+static void
+pam_cleanup(void *arg)
+{
+ (void)arg;
+ debug("PAM: cleanup");
+ if (pam_cred_established) {
+ pam_setcred(pam_handle, PAM_DELETE_CRED);
+ pam_cred_established = 0;
+ }
+ if (pam_session_open) {
+ pam_close_session(pam_handle, PAM_SILENT);
+ pam_session_open = 0;
+ }
+ pam_authenticated = pam_new_authtok_reqd = 0;
+ pam_end(pam_handle, pam_err);
+ pam_handle = NULL;
+}
+
+static int
+pam_init(const char *user)
+{
+ extern ServerOptions options;
+ extern u_int utmp_len;
+ const char *pam_rhost, *pam_user;
+
+ if (pam_handle != NULL) {
+ /* We already have a PAM context; check if the user matches */
+ pam_err = pam_get_item(pam_handle,
+ PAM_USER, (const void **)&pam_user);
+ if (pam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
+ return (0);
+ fatal_remove_cleanup(pam_cleanup, NULL);
+ pam_end(pam_handle, pam_err);
+ pam_handle = NULL;
+ }
+ debug("PAM: initializing for \"%s\"", user);
+ pam_err = pam_start("sshd", user, NULL, &pam_handle);
+ if (pam_err != PAM_SUCCESS)
+ return (-1);
+ pam_rhost = get_remote_name_or_ip(utmp_len,
+ options.verify_reverse_mapping);
+ debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
+ pam_err = pam_set_item(pam_handle, PAM_RHOST, pam_rhost);
+ if (pam_err != PAM_SUCCESS) {
+ pam_end(pam_handle, pam_err);
+ pam_handle = NULL;
+ return (-1);
+ }
+ fatal_add_cleanup(pam_cleanup, NULL);
+ return (0);
}
static void *
@@ -167,38 +278,33 @@ pam_init_ctx(Authctxt *authctxt)
{
struct pam_ctxt *ctxt;
int socks[2];
- int i;
+
+ /* Initialize PAM */
+ if (pam_init(authctxt->user) == -1) {
+ error("PAM: initialization failed");
+ return (NULL);
+ }
ctxt = xmalloc(sizeof *ctxt);
- ctxt->pam_user = xstrdup(authctxt->user);
ctxt->pam_done = 0;
+
+ /* Start the authentication thread */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
- error("%s: failed create sockets: %s",
- __func__, strerror(errno));
+ error("PAM: failed create sockets: %s", strerror(errno));
xfree(ctxt);
return (NULL);
}
- if ((ctxt->pam_pid = fork()) == -1) {
- error("%s: failed to fork auth-pam child: %s",
- __func__, strerror(errno));
+ if (pthread_create(&ctxt->pam_thread, NULL, pam_thread, ctxt) == -1) {
+ error("PAM: failed to start authentication thread: %s",
+ strerror(errno));
close(socks[0]);
close(socks[1]);
xfree(ctxt);
return (NULL);
}
- if (ctxt->pam_pid == 0) {
- /* close everything except our end of the pipe */
- ctxt->pam_sock = socks[1];
- for (i = 3; i < getdtablesize(); ++i)
- if (i != ctxt->pam_sock)
- close(i);
- pam_child(ctxt);
- /* not reached */
- exit(1);
- }
- ctxt->pam_sock = socks[0];
- close(socks[1]);
- fatal_add_cleanup(pam_cleanup, ctxt);
+ ctxt->pam_psock = socks[0];
+ ctxt->pam_csock = socks[1];
+ fatal_add_cleanup(pam_thread_cleanup, ctxt);
return (ctxt);
}
@@ -219,7 +325,7 @@ pam_query(void *ctx, char **name, char **info,
**prompts = NULL;
plen = 0;
*echo_on = xmalloc(sizeof(u_int));
- while (ssh_msg_recv(ctxt->pam_sock, &buffer) == 0) {
+ while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
type = buffer_get_char(&buffer);
msg = buffer_get_string(&buffer, NULL);
switch (type) {
@@ -259,7 +365,7 @@ pam_query(void *ctx, char **name, char **info,
xfree(msg);
return (0);
}
- error("%s", msg);
+ error("PAM: %s", msg);
default:
*num = 0;
**echo_on = 0;
@@ -278,9 +384,10 @@ pam_respond(void *ctx, u_int num, char **resp)
struct pam_ctxt *ctxt = ctx;
char *msg;
- debug2(__func__);
+ debug2("PAM: %s", __func__);
switch (ctxt->pam_done) {
case 1:
+ pam_authenticated = 1;
return (0);
case 0:
break;
@@ -288,12 +395,12 @@ pam_respond(void *ctx, u_int num, char **resp)
return (-1);
}
if (num != 1) {
- error("expected one response, got %u", num);
+ error("PAM: expected one response, got %u", num);
return (-1);
}
buffer_init(&buffer);
buffer_put_cstring(&buffer, *resp);
- ssh_msg_send(ctxt->pam_sock, PAM_AUTHTOK, &buffer);
+ ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer);
buffer_free(&buffer);
return (1);
}
@@ -302,14 +409,16 @@ static void
pam_free_ctx(void *ctxtp)
{
struct pam_ctxt *ctxt = ctxtp;
- int status;
- fatal_remove_cleanup(pam_cleanup, ctxt);
- close(ctxt->pam_sock);
- kill(ctxt->pam_pid, SIGHUP);
- waitpid(ctxt->pam_pid, &status, 0);
- xfree(ctxt->pam_user);
+ fatal_remove_cleanup(pam_thread_cleanup, ctxt);
+ pam_thread_cleanup(ctxtp);
xfree(ctxt);
+ /*
+ * We don't call pam_cleanup() here because we may need the PAM
+ * handle at a later stage, e.g. when setting up a session. It's
+ * still on the cleanup list, so pam_end() *will* be called before
+ * the server process terminates.
+ */
}
KbdintDevice pam_device = {
@@ -328,4 +437,164 @@ KbdintDevice mm_pam_device = {
mm_pam_free_ctx
};
+/*
+ * This replaces auth-pam.c
+ */
+void
+start_pam(const char *user)
+{
+ if (pam_init(user) == -1)
+ fatal("PAM: initialisation failed");
+}
+
+void
+finish_pam(void)
+{
+ fatal_remove_cleanup(pam_cleanup, NULL);
+ pam_cleanup(NULL);
+}
+
+int
+do_pam_account(const char *user, const char *ruser)
+{
+ /* XXX */
+ return (1);
+}
+
+void
+do_pam_session(const char *user, const char *tty)
+{
+ debug("PAM: setting PAM_TTY to \"%s\"", tty);
+ pam_err = pam_set_item(pam_handle, PAM_TTY, tty);
+ if (pam_err != PAM_SUCCESS)
+ fatal("PAM: failed to set PAM_TTY: %s",
+ pam_strerror(pam_handle, pam_err));
+ pam_err = pam_open_session(pam_handle, 0);
+ if (pam_err != PAM_SUCCESS)
+ fatal("PAM: pam_open_session(): %s",
+ pam_strerror(pam_handle, pam_err));
+ pam_session_open = 1;
+}
+
+void
+do_pam_setcred(int init)
+{
+ if (init) {
+ debug("PAM: establishing credentials");
+ pam_err = pam_setcred(pam_handle, PAM_ESTABLISH_CRED);
+ } else {
+ debug("PAM: reinitializing credentials");
+ pam_err = pam_setcred(pam_handle, PAM_REINITIALIZE_CRED);
+ }
+ if (pam_err == PAM_SUCCESS) {
+ pam_cred_established = 1;
+ return;
+ }
+ if (pam_authenticated)
+ fatal("PAM: pam_setcred(): %s",
+ pam_strerror(pam_handle, pam_err));
+ else
+ debug("PAM: pam_setcred(): %s",
+ pam_strerror(pam_handle, pam_err));
+}
+
+int
+is_pam_password_change_required(void)
+{
+ return (pam_new_authtok_reqd);
+}
+
+static int
+pam_chauthtok_conv(int n,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *data)
+{
+ char input[PAM_MAX_MSG_SIZE];
+ int i;
+
+ if (n <= 0 || n > PAM_MAX_NUM_MSG)
+ return (PAM_CONV_ERR);
+ *resp = xmalloc(n * sizeof **resp);
+ for (i = 0; i < n; ++i) {
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ resp[i]->resp =
+ read_passphrase(msg[i]->msg, RP_ALLOW_STDIN);
+ resp[i]->resp_retcode = PAM_SUCCESS;
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ fputs(msg[i]->msg, stderr);
+ fgets(input, sizeof input, stdin);
+ resp[i]->resp = xstrdup(input);
+ resp[i]->resp_retcode = PAM_SUCCESS;
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ fputs(msg[i]->msg, stderr);
+ resp[i]->resp_retcode = PAM_SUCCESS;
+ break;
+ default:
+ goto fail;
+ }
+ }
+ return (PAM_SUCCESS);
+ fail:
+ while (i)
+ xfree(resp[--i]);
+ xfree(*resp);
+ *resp = NULL;
+ return (PAM_CONV_ERR);
+}
+
+/*
+ * 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_chauthtok_conv, NULL };
+
+ if (use_privsep)
+ fatal("PAM: chauthtok not supprted with privsep");
+ pam_err = pam_set_item(pam_handle,
+ PAM_CONV, (const void *)&pam_conv);
+ if (pam_err != PAM_SUCCESS)
+ fatal("PAM: failed to set PAM_CONV: %s",
+ pam_strerror(pam_handle, pam_err));
+ debug("PAM: changing password");
+ pam_err = pam_chauthtok(pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
+ if (pam_err != PAM_SUCCESS)
+ fatal("PAM: pam_chauthtok(): %s",
+ pam_strerror(pam_handle, pam_err));
+}
+
+void
+print_pam_messages(void)
+{
+ /* XXX */
+}
+
+char **
+fetch_pam_environment(void)
+{
+#ifdef HAVE_PAM_GETENVLIST
+ debug("PAM: retrieving environment");
+ return (pam_getenvlist(pam_handle));
+#else
+ return (NULL);
+#endif
+}
+
+void
+free_pam_environment(char **env)
+{
+ char **envp;
+
+ for (envp = env; *envp; envp++)
+ xfree(*envp);
+ xfree(env);
+}
+
#endif /* USE_PAM */
OpenPOWER on IntegriCloud