diff options
author | des <des@FreeBSD.org> | 2002-12-14 13:52:39 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2002-12-14 13:52:39 +0000 |
commit | 06b0ce4f651a97f9ac03d4ba5be051af78b485a7 (patch) | |
tree | 151177d747202a0dea48317bf08a5027dcfa7067 /crypto/openssh | |
parent | b6985eb271ae3a1dc1a45d1bfe3c3e9a56b283fb (diff) | |
download | FreeBSD-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/openssh')
-rw-r--r-- | crypto/openssh/auth-pam.h | 4 | ||||
-rw-r--r-- | crypto/openssh/auth2-pam-freebsd.c | 395 |
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 */ |