1cf2b5f3bSDag-Erling Smørgrav /*- 2cf2b5f3bSDag-Erling Smørgrav * Copyright (c) 2002 Networks Associates Technology, Inc. 3cf2b5f3bSDag-Erling Smørgrav * All rights reserved. 4cf2b5f3bSDag-Erling Smørgrav * 5cf2b5f3bSDag-Erling Smørgrav * This software was developed for the FreeBSD Project by ThinkSec AS and 6cf2b5f3bSDag-Erling Smørgrav * NAI Labs, the Security Research Division of Network Associates, Inc. 7cf2b5f3bSDag-Erling Smørgrav * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 8cf2b5f3bSDag-Erling Smørgrav * DARPA CHATS research program. 909958426SBrian Feldman * 1009958426SBrian Feldman * Redistribution and use in source and binary forms, with or without 1109958426SBrian Feldman * modification, are permitted provided that the following conditions 1209958426SBrian Feldman * are met: 1309958426SBrian Feldman * 1. Redistributions of source code must retain the above copyright 1409958426SBrian Feldman * notice, this list of conditions and the following disclaimer. 1509958426SBrian Feldman * 2. Redistributions in binary form must reproduce the above copyright 1609958426SBrian Feldman * notice, this list of conditions and the following disclaimer in the 1709958426SBrian Feldman * documentation and/or other materials provided with the distribution. 1809958426SBrian Feldman * 19cf2b5f3bSDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20cf2b5f3bSDag-Erling Smørgrav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21cf2b5f3bSDag-Erling Smørgrav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22cf2b5f3bSDag-Erling Smørgrav * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23cf2b5f3bSDag-Erling Smørgrav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24cf2b5f3bSDag-Erling Smørgrav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25cf2b5f3bSDag-Erling Smørgrav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26cf2b5f3bSDag-Erling Smørgrav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27cf2b5f3bSDag-Erling Smørgrav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28cf2b5f3bSDag-Erling Smørgrav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29cf2b5f3bSDag-Erling Smørgrav * SUCH DAMAGE. 3009958426SBrian Feldman */ 3121e764dfSDag-Erling Smørgrav /* 3221e764dfSDag-Erling Smørgrav * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org> 3321e764dfSDag-Erling Smørgrav * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au> 3421e764dfSDag-Erling Smørgrav * 3521e764dfSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 3621e764dfSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 3721e764dfSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 3821e764dfSDag-Erling Smørgrav * 3921e764dfSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 4021e764dfSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 4121e764dfSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 4221e764dfSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 4321e764dfSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 4421e764dfSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 4521e764dfSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 4621e764dfSDag-Erling Smørgrav */ 4709958426SBrian Feldman 48acc1a9efSDag-Erling Smørgrav /* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */ 49acc1a9efSDag-Erling Smørgrav 5009958426SBrian Feldman #include "includes.h" 51333ee039SDag-Erling Smørgrav 52333ee039SDag-Erling Smørgrav #include <sys/types.h> 53333ee039SDag-Erling Smørgrav #include <sys/stat.h> 54333ee039SDag-Erling Smørgrav #include <sys/wait.h> 55333ee039SDag-Erling Smørgrav 56333ee039SDag-Erling Smørgrav #include <errno.h> 57333ee039SDag-Erling Smørgrav #include <signal.h> 58333ee039SDag-Erling Smørgrav #include <stdarg.h> 5919261079SEd Maste #include <stdlib.h> 60333ee039SDag-Erling Smørgrav #include <string.h> 61333ee039SDag-Erling Smørgrav #include <unistd.h> 6209958426SBrian Feldman 6309958426SBrian Feldman #ifdef USE_PAM 641ec0d754SDag-Erling Smørgrav #if defined(HAVE_SECURITY_PAM_APPL_H) 65cf2b5f3bSDag-Erling Smørgrav #include <security/pam_appl.h> 661ec0d754SDag-Erling Smørgrav #elif defined (HAVE_PAM_PAM_APPL_H) 671ec0d754SDag-Erling Smørgrav #include <pam/pam_appl.h> 681ec0d754SDag-Erling Smørgrav #endif 69cf2b5f3bSDag-Erling Smørgrav 70ca86bcf2SDag-Erling Smørgrav #if !defined(SSHD_PAM_SERVICE) 71ca86bcf2SDag-Erling Smørgrav extern char *__progname; 72ca86bcf2SDag-Erling Smørgrav # define SSHD_PAM_SERVICE __progname 73ca86bcf2SDag-Erling Smørgrav #endif 74ca86bcf2SDag-Erling Smørgrav 75d4ecd108SDag-Erling Smørgrav /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ 76d4ecd108SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE 77076ad2f8SDag-Erling Smørgrav # define sshpam_const /* Solaris, HP-UX, SunOS */ 78d4ecd108SDag-Erling Smørgrav #else 79076ad2f8SDag-Erling Smørgrav # define sshpam_const const /* LinuxPAM, OpenPAM, AIX */ 80d4ecd108SDag-Erling Smørgrav #endif 81d4ecd108SDag-Erling Smørgrav 82333ee039SDag-Erling Smørgrav /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ 83333ee039SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE 84333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) 85333ee039SDag-Erling Smørgrav #else 86333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) 87333ee039SDag-Erling Smørgrav #endif 88333ee039SDag-Erling Smørgrav 89333ee039SDag-Erling Smørgrav #include "xmalloc.h" 90190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 91190cef3dSDag-Erling Smørgrav #include "ssherr.h" 92333ee039SDag-Erling Smørgrav #include "hostfile.h" 93989dd127SDag-Erling Smørgrav #include "auth.h" 94989dd127SDag-Erling Smørgrav #include "auth-pam.h" 954c5de869SBrian Feldman #include "canohost.h" 96cf2b5f3bSDag-Erling Smørgrav #include "log.h" 97cf2b5f3bSDag-Erling Smørgrav #include "msg.h" 98cf2b5f3bSDag-Erling Smørgrav #include "packet.h" 9921e764dfSDag-Erling Smørgrav #include "misc.h" 100cf2b5f3bSDag-Erling Smørgrav #include "servconf.h" 101cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h" 102cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h" 10319261079SEd Maste #include "misc.h" 104333ee039SDag-Erling Smørgrav #ifdef GSSAPI 105333ee039SDag-Erling Smørgrav #include "ssh-gss.h" 106333ee039SDag-Erling Smørgrav #endif 107333ee039SDag-Erling Smørgrav #include "monitor_wrap.h" 108b2af61ecSKurt Lidl #include "blacklist_client.h" 10909958426SBrian Feldman 110989dd127SDag-Erling Smørgrav extern ServerOptions options; 111190cef3dSDag-Erling Smørgrav extern struct sshbuf *loginmsg; 1125962c0e9SDag-Erling Smørgrav extern u_int utmp_len; 1132c917d39SAlfred Perlstein 114aa49c926SDag-Erling Smørgrav /* so we don't silently change behaviour */ 115cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS 116aa49c926SDag-Erling Smørgrav # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK" 117aa49c926SDag-Erling Smørgrav #endif 118aa49c926SDag-Erling Smørgrav 119aa49c926SDag-Erling Smørgrav /* 120aa49c926SDag-Erling Smørgrav * Formerly known as USE_POSIX_THREADS, using this is completely unsupported 121aa49c926SDag-Erling Smørgrav * and generally a bad idea. Use at own risk and do not expect support if 122aa49c926SDag-Erling Smørgrav * this breaks. 123aa49c926SDag-Erling Smørgrav */ 124aa49c926SDag-Erling Smørgrav #ifdef UNSUPPORTED_POSIX_THREADS_HACK 125cf2b5f3bSDag-Erling Smørgrav #include <pthread.h> 126cf2b5f3bSDag-Erling Smørgrav /* 127cf2b5f3bSDag-Erling Smørgrav * Avoid namespace clash when *not* using pthreads for systems *with* 128cf2b5f3bSDag-Erling Smørgrav * pthreads, which unconditionally define pthread_t via sys/types.h 129cf2b5f3bSDag-Erling Smørgrav * (e.g. Linux) 130cf2b5f3bSDag-Erling Smørgrav */ 131cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t; 132cf2b5f3bSDag-Erling Smørgrav #else 1331ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t; 1342a01feabSEd Maste #define pthread_exit fake_pthread_exit 1352a01feabSEd Maste #define pthread_create fake_pthread_create 1362a01feabSEd Maste #define pthread_cancel fake_pthread_cancel 1372a01feabSEd Maste #define pthread_join fake_pthread_join 1381ec0d754SDag-Erling Smørgrav #endif 1391ec0d754SDag-Erling Smørgrav 1401ec0d754SDag-Erling Smørgrav struct pam_ctxt { 1411ec0d754SDag-Erling Smørgrav sp_pthread_t pam_thread; 1421ec0d754SDag-Erling Smørgrav int pam_psock; 1431ec0d754SDag-Erling Smørgrav int pam_csock; 1441ec0d754SDag-Erling Smørgrav int pam_done; 1451ec0d754SDag-Erling Smørgrav }; 1461ec0d754SDag-Erling Smørgrav 1471ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *); 1481ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt; 1491ec0d754SDag-Erling Smørgrav 150aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 151cf2b5f3bSDag-Erling Smørgrav /* 152cf2b5f3bSDag-Erling Smørgrav * Simulate threads with processes. 153cf2b5f3bSDag-Erling Smørgrav */ 1541ec0d754SDag-Erling Smørgrav 1551ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1; 15619261079SEd Maste static sshsig_t sshpam_oldsig; 1571ec0d754SDag-Erling Smørgrav 1581ec0d754SDag-Erling Smørgrav static void 1591ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig) 1601ec0d754SDag-Erling Smørgrav { 16119261079SEd Maste ssh_signal(SIGCHLD, SIG_DFL); 1621ec0d754SDag-Erling Smørgrav if (cleanup_ctxt == NULL) 1631ec0d754SDag-Erling Smørgrav return; /* handler called after PAM cleanup, shouldn't happen */ 16421e764dfSDag-Erling Smørgrav if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) 16521e764dfSDag-Erling Smørgrav <= 0) { 16621e764dfSDag-Erling Smørgrav /* PAM thread has not exitted, privsep slave must have */ 16721e764dfSDag-Erling Smørgrav kill(cleanup_ctxt->pam_thread, SIGTERM); 168076ad2f8SDag-Erling Smørgrav while (waitpid(cleanup_ctxt->pam_thread, 169076ad2f8SDag-Erling Smørgrav &sshpam_thread_status, 0) == -1) { 170076ad2f8SDag-Erling Smørgrav if (errno == EINTR) 171076ad2f8SDag-Erling Smørgrav continue; 172076ad2f8SDag-Erling Smørgrav return; 173076ad2f8SDag-Erling Smørgrav } 17421e764dfSDag-Erling Smørgrav } 1751ec0d754SDag-Erling Smørgrav if (WIFSIGNALED(sshpam_thread_status) && 1761ec0d754SDag-Erling Smørgrav WTERMSIG(sshpam_thread_status) == SIGTERM) 1771ec0d754SDag-Erling Smørgrav return; /* terminated by pthread_cancel */ 1781ec0d754SDag-Erling Smørgrav if (!WIFEXITED(sshpam_thread_status)) 179d4af9e69SDag-Erling Smørgrav sigdie("PAM: authentication thread exited unexpectedly"); 1801ec0d754SDag-Erling Smørgrav if (WEXITSTATUS(sshpam_thread_status) != 0) 181d4af9e69SDag-Erling Smørgrav sigdie("PAM: authentication thread exited uncleanly"); 1821ec0d754SDag-Erling Smørgrav } 18309958426SBrian Feldman 184333ee039SDag-Erling Smørgrav /* ARGSUSED */ 185cf2b5f3bSDag-Erling Smørgrav static void 186333ee039SDag-Erling Smørgrav pthread_exit(void *value) 18709958426SBrian Feldman { 188cf2b5f3bSDag-Erling Smørgrav _exit(0); 18909958426SBrian Feldman } 19009958426SBrian Feldman 191333ee039SDag-Erling Smørgrav /* ARGSUSED */ 192cf2b5f3bSDag-Erling Smørgrav static int 193333ee039SDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr, 194cf2b5f3bSDag-Erling Smørgrav void *(*thread_start)(void *), void *arg) 195cf2b5f3bSDag-Erling Smørgrav { 196cf2b5f3bSDag-Erling Smørgrav pid_t pid; 197d4ecd108SDag-Erling Smørgrav struct pam_ctxt *ctx = arg; 198cf2b5f3bSDag-Erling Smørgrav 1995962c0e9SDag-Erling Smørgrav sshpam_thread_status = -1; 200cf2b5f3bSDag-Erling Smørgrav switch ((pid = fork())) { 201cf2b5f3bSDag-Erling Smørgrav case -1: 202cf2b5f3bSDag-Erling Smørgrav error("fork(): %s", strerror(errno)); 20319261079SEd Maste return errno; 204cf2b5f3bSDag-Erling Smørgrav case 0: 205d4ecd108SDag-Erling Smørgrav close(ctx->pam_psock); 206d4ecd108SDag-Erling Smørgrav ctx->pam_psock = -1; 207cf2b5f3bSDag-Erling Smørgrav thread_start(arg); 208cf2b5f3bSDag-Erling Smørgrav _exit(1); 209cf2b5f3bSDag-Erling Smørgrav default: 210cf2b5f3bSDag-Erling Smørgrav *thread = pid; 211d4ecd108SDag-Erling Smørgrav close(ctx->pam_csock); 212d4ecd108SDag-Erling Smørgrav ctx->pam_csock = -1; 21319261079SEd Maste sshpam_oldsig = ssh_signal(SIGCHLD, sshpam_sigchld_handler); 214cf2b5f3bSDag-Erling Smørgrav return (0); 215cf2b5f3bSDag-Erling Smørgrav } 216cf2b5f3bSDag-Erling Smørgrav } 217cf2b5f3bSDag-Erling Smørgrav 218cf2b5f3bSDag-Erling Smørgrav static int 219cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread) 220cf2b5f3bSDag-Erling Smørgrav { 22119261079SEd Maste ssh_signal(SIGCHLD, sshpam_oldsig); 222cf2b5f3bSDag-Erling Smørgrav return (kill(thread, SIGTERM)); 223cf2b5f3bSDag-Erling Smørgrav } 224cf2b5f3bSDag-Erling Smørgrav 225333ee039SDag-Erling Smørgrav /* ARGSUSED */ 226cf2b5f3bSDag-Erling Smørgrav static int 227333ee039SDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value) 228cf2b5f3bSDag-Erling Smørgrav { 229cf2b5f3bSDag-Erling Smørgrav int status; 230cf2b5f3bSDag-Erling Smørgrav 2311ec0d754SDag-Erling Smørgrav if (sshpam_thread_status != -1) 2321ec0d754SDag-Erling Smørgrav return (sshpam_thread_status); 23319261079SEd Maste ssh_signal(SIGCHLD, sshpam_oldsig); 234076ad2f8SDag-Erling Smørgrav while (waitpid(thread, &status, 0) == -1) { 235076ad2f8SDag-Erling Smørgrav if (errno == EINTR) 236076ad2f8SDag-Erling Smørgrav continue; 237076ad2f8SDag-Erling Smørgrav fatal("%s: waitpid: %s", __func__, strerror(errno)); 238076ad2f8SDag-Erling Smørgrav } 239cf2b5f3bSDag-Erling Smørgrav return (status); 240cf2b5f3bSDag-Erling Smørgrav } 241cf2b5f3bSDag-Erling Smørgrav #endif 242cf2b5f3bSDag-Erling Smørgrav 243cf2b5f3bSDag-Erling Smørgrav 244cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL; 245cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0; 246cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0; 247cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0; 248cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0; 2491ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1; 250076ad2f8SDag-Erling Smørgrav static int sshpam_maxtries_reached = 0; 2511ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL; 2525962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL; 25321e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL; 25419261079SEd Maste static char *sshpam_rhost = NULL; 25519261079SEd Maste static char *sshpam_laddr = NULL; 256cf2b5f3bSDag-Erling Smørgrav 2571ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */ 2581ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST 2591ec0d754SDag-Erling Smørgrav static char ** 2601ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh) 2611ec0d754SDag-Erling Smørgrav { 2621ec0d754SDag-Erling Smørgrav /* 26319261079SEd Maste * XXX - If necessary, we can still support environment passing 2641ec0d754SDag-Erling Smørgrav * for platforms without pam_getenvlist by searching for known 2651ec0d754SDag-Erling Smørgrav * env vars (e.g. KRB5CCNAME) from the PAM environment. 2661ec0d754SDag-Erling Smørgrav */ 2671ec0d754SDag-Erling Smørgrav return NULL; 2681ec0d754SDag-Erling Smørgrav } 2691ec0d754SDag-Erling Smørgrav #endif 270cf2b5f3bSDag-Erling Smørgrav 27119261079SEd Maste #ifndef HAVE_PAM_PUTENV 27219261079SEd Maste static int 27319261079SEd Maste pam_putenv(pam_handle_t *pamh, const char *name_value) 27419261079SEd Maste { 27519261079SEd Maste return PAM_SUCCESS; 27619261079SEd Maste } 27719261079SEd Maste #endif /* HAVE_PAM_PUTENV */ 27819261079SEd Maste 27921e764dfSDag-Erling Smørgrav /* 28021e764dfSDag-Erling Smørgrav * Some platforms, notably Solaris, do not enforce password complexity 28121e764dfSDag-Erling Smørgrav * rules during pam_chauthtok() if the real uid of the calling process 28221e764dfSDag-Erling Smørgrav * is 0, on the assumption that it's being called by "passwd" run by root. 28321e764dfSDag-Erling Smørgrav * This wraps pam_chauthtok and sets/restore the real uid so PAM will do 28421e764dfSDag-Erling Smørgrav * the right thing. 28521e764dfSDag-Erling Smørgrav */ 28621e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID 28721e764dfSDag-Erling Smørgrav static int 28821e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) 28921e764dfSDag-Erling Smørgrav { 29021e764dfSDag-Erling Smørgrav int result; 29121e764dfSDag-Erling Smørgrav 29221e764dfSDag-Erling Smørgrav if (sshpam_authctxt == NULL) 29321e764dfSDag-Erling Smørgrav fatal("PAM: sshpam_authctxt not initialized"); 29421e764dfSDag-Erling Smørgrav if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) 29521e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 29621e764dfSDag-Erling Smørgrav result = pam_chauthtok(pamh, flags); 29721e764dfSDag-Erling Smørgrav if (setreuid(0, -1) == -1) 29821e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 29921e764dfSDag-Erling Smørgrav return result; 30021e764dfSDag-Erling Smørgrav } 30121e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) 30221e764dfSDag-Erling Smørgrav #endif 30321e764dfSDag-Erling Smørgrav 30419261079SEd Maste static void 30521e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd) 3061ec0d754SDag-Erling Smørgrav { 30747dd1d1bSDag-Erling Smørgrav extern struct sshauthopt *auth_opts; 30847dd1d1bSDag-Erling Smørgrav static int saved_port, saved_agent, saved_x11; 30947dd1d1bSDag-Erling Smørgrav 3101ec0d754SDag-Erling Smørgrav debug3("%s %d", __func__, reqd); 3115962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 3125962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 3135962c0e9SDag-Erling Smørgrav sshpam_authctxt->force_pwchange = reqd; 3141ec0d754SDag-Erling Smørgrav if (reqd) { 31547dd1d1bSDag-Erling Smørgrav saved_port = auth_opts->permit_port_forwarding_flag; 31647dd1d1bSDag-Erling Smørgrav saved_agent = auth_opts->permit_agent_forwarding_flag; 31747dd1d1bSDag-Erling Smørgrav saved_x11 = auth_opts->permit_x11_forwarding_flag; 31847dd1d1bSDag-Erling Smørgrav auth_opts->permit_port_forwarding_flag = 0; 31947dd1d1bSDag-Erling Smørgrav auth_opts->permit_agent_forwarding_flag = 0; 32047dd1d1bSDag-Erling Smørgrav auth_opts->permit_x11_forwarding_flag = 0; 3211ec0d754SDag-Erling Smørgrav } else { 32247dd1d1bSDag-Erling Smørgrav if (saved_port) 32347dd1d1bSDag-Erling Smørgrav auth_opts->permit_port_forwarding_flag = saved_port; 32447dd1d1bSDag-Erling Smørgrav if (saved_agent) 32547dd1d1bSDag-Erling Smørgrav auth_opts->permit_agent_forwarding_flag = saved_agent; 32647dd1d1bSDag-Erling Smørgrav if (saved_x11) 32747dd1d1bSDag-Erling Smørgrav auth_opts->permit_x11_forwarding_flag = saved_x11; 3281ec0d754SDag-Erling Smørgrav } 3291ec0d754SDag-Erling Smørgrav } 3301ec0d754SDag-Erling Smørgrav 3311ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */ 3321ec0d754SDag-Erling Smørgrav static void 333190cef3dSDag-Erling Smørgrav import_environments(struct sshbuf *b) 3341ec0d754SDag-Erling Smørgrav { 3351ec0d754SDag-Erling Smørgrav char *env; 336190cef3dSDag-Erling Smørgrav u_int n, i, num_env; 337190cef3dSDag-Erling Smørgrav int r; 3381ec0d754SDag-Erling Smørgrav 3391ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 3401ec0d754SDag-Erling Smørgrav 341aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 3421ec0d754SDag-Erling Smørgrav /* Import variables set by do_pam_account */ 343190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &n)) != 0) 344190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 345190cef3dSDag-Erling Smørgrav if (n > INT_MAX) 346190cef3dSDag-Erling Smørgrav fatal("%s: invalid PAM account status %u", __func__, n); 347190cef3dSDag-Erling Smørgrav sshpam_account_status = (int)n; 348190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &n)) != 0) 349190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 350190cef3dSDag-Erling Smørgrav sshpam_password_change_required(n != 0); 3511ec0d754SDag-Erling Smørgrav 3521ec0d754SDag-Erling Smørgrav /* Import environment from subprocess */ 353190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &num_env)) != 0) 354190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 355333ee039SDag-Erling Smørgrav if (num_env > 1024) 356333ee039SDag-Erling Smørgrav fatal("%s: received %u environment variables, expected <= 1024", 357333ee039SDag-Erling Smørgrav __func__, num_env); 358333ee039SDag-Erling Smørgrav sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); 3591ec0d754SDag-Erling Smørgrav debug3("PAM: num env strings %d", num_env); 360190cef3dSDag-Erling Smørgrav for(i = 0; i < num_env; i++) { 361190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(b, &(sshpam_env[i]), NULL)) != 0) 362190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 363190cef3dSDag-Erling Smørgrav } 3641ec0d754SDag-Erling Smørgrav sshpam_env[num_env] = NULL; 3651ec0d754SDag-Erling Smørgrav 3661ec0d754SDag-Erling Smørgrav /* Import PAM environment from subprocess */ 367190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &num_env)) != 0) 368190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 3691ec0d754SDag-Erling Smørgrav debug("PAM: num PAM env strings %d", num_env); 3701ec0d754SDag-Erling Smørgrav for (i = 0; i < num_env; i++) { 371190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(b, &env, NULL)) != 0) 372190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 3731ec0d754SDag-Erling Smørgrav /* Errors are not fatal here */ 374190cef3dSDag-Erling Smørgrav if ((r = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { 3751ec0d754SDag-Erling Smørgrav error("PAM: pam_putenv: %s", 376190cef3dSDag-Erling Smørgrav pam_strerror(sshpam_handle, r)); 3771ec0d754SDag-Erling Smørgrav } 37819261079SEd Maste /* 37919261079SEd Maste * XXX this possibly leaks env because it is not documented 38019261079SEd Maste * what pam_putenv() does with it. Does it copy it? Does it 38119261079SEd Maste * take ownweship? We don't know, so it's safest just to leak. 38219261079SEd Maste */ 3831ec0d754SDag-Erling Smørgrav } 3845962c0e9SDag-Erling Smørgrav #endif 3851ec0d754SDag-Erling Smørgrav } 386cf2b5f3bSDag-Erling Smørgrav 387cf2b5f3bSDag-Erling Smørgrav /* 388cf2b5f3bSDag-Erling Smørgrav * Conversation function for authentication thread. 389cf2b5f3bSDag-Erling Smørgrav */ 390cf2b5f3bSDag-Erling Smørgrav static int 391d4ecd108SDag-Erling Smørgrav sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, 392cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 393cf2b5f3bSDag-Erling Smørgrav { 394190cef3dSDag-Erling Smørgrav struct sshbuf *buffer; 395cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 396cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 397190cef3dSDag-Erling Smørgrav int r, i; 398190cef3dSDag-Erling Smørgrav u_char status; 399cf2b5f3bSDag-Erling Smørgrav 4001ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 401cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 402cf2b5f3bSDag-Erling Smørgrav 40321e764dfSDag-Erling Smørgrav if (data == NULL) { 40421e764dfSDag-Erling Smørgrav error("PAM: conversation function passed a null context"); 40521e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 40621e764dfSDag-Erling Smørgrav } 407cf2b5f3bSDag-Erling Smørgrav ctxt = data; 408cf2b5f3bSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 409cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 410cf2b5f3bSDag-Erling Smørgrav 411333ee039SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 412190cef3dSDag-Erling Smørgrav return PAM_CONV_ERR; 413190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) { 414190cef3dSDag-Erling Smørgrav free(reply); 415190cef3dSDag-Erling Smørgrav return PAM_CONV_ERR; 416190cef3dSDag-Erling Smørgrav } 417cf2b5f3bSDag-Erling Smørgrav 418cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 419cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 420cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 421cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 422190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, 423190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 424190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 425190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 4261ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 427190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) 4281ec0d754SDag-Erling Smørgrav goto fail; 429190cef3dSDag-Erling Smørgrav 430190cef3dSDag-Erling Smørgrav if (ssh_msg_recv(ctxt->pam_csock, buffer) == -1) 4311ec0d754SDag-Erling Smørgrav goto fail; 432190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u8(buffer, &status)) != 0) 433190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 434190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 435190cef3dSDag-Erling Smørgrav if (status != PAM_AUTHTOK) 436cf2b5f3bSDag-Erling Smørgrav goto fail; 437190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(buffer, 438190cef3dSDag-Erling Smørgrav &reply[i].resp, NULL)) != 0) 439190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 440190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 441cf2b5f3bSDag-Erling Smørgrav break; 442cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 443cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 444190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, 445190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 446190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 447190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 4481ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 449190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) 4501ec0d754SDag-Erling Smørgrav goto fail; 451cf2b5f3bSDag-Erling Smørgrav break; 452cf2b5f3bSDag-Erling Smørgrav default: 453cf2b5f3bSDag-Erling Smørgrav goto fail; 454cf2b5f3bSDag-Erling Smørgrav } 455190cef3dSDag-Erling Smørgrav sshbuf_reset(buffer); 456cf2b5f3bSDag-Erling Smørgrav } 457190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 458cf2b5f3bSDag-Erling Smørgrav *resp = reply; 459cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 460cf2b5f3bSDag-Erling Smørgrav 461cf2b5f3bSDag-Erling Smørgrav fail: 462cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 463e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 464cf2b5f3bSDag-Erling Smørgrav } 465e4a9863fSDag-Erling Smørgrav free(reply); 466190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 467cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 468cf2b5f3bSDag-Erling Smørgrav } 469cf2b5f3bSDag-Erling Smørgrav 470cf2b5f3bSDag-Erling Smørgrav /* 471cf2b5f3bSDag-Erling Smørgrav * Authentication thread. 472cf2b5f3bSDag-Erling Smørgrav */ 473cf2b5f3bSDag-Erling Smørgrav static void * 474cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp) 475cf2b5f3bSDag-Erling Smørgrav { 476cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 477190cef3dSDag-Erling Smørgrav struct sshbuf *buffer = NULL; 478cf2b5f3bSDag-Erling Smørgrav struct pam_conv sshpam_conv; 479190cef3dSDag-Erling Smørgrav int r, flags = (options.permit_empty_passwd == 0 ? 48021e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 481aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 4821ec0d754SDag-Erling Smørgrav extern char **environ; 4831ec0d754SDag-Erling Smørgrav char **env_from_pam; 4841ec0d754SDag-Erling Smørgrav u_int i; 485cf2b5f3bSDag-Erling Smørgrav const char *pam_user; 486d4ecd108SDag-Erling Smørgrav const char **ptr_pam_user = &pam_user; 487333ee039SDag-Erling Smørgrav char *tz = getenv("TZ"); 488cf2b5f3bSDag-Erling Smørgrav 489f7167e0eSDag-Erling Smørgrav sshpam_err = pam_get_item(sshpam_handle, PAM_USER, 490d4ecd108SDag-Erling Smørgrav (sshpam_const void **)ptr_pam_user); 491f7167e0eSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 492f7167e0eSDag-Erling Smørgrav goto auth_fail; 493333ee039SDag-Erling Smørgrav 4941ec0d754SDag-Erling Smørgrav environ[0] = NULL; 495333ee039SDag-Erling Smørgrav if (tz != NULL) 496333ee039SDag-Erling Smørgrav if (setenv("TZ", tz, 1) == -1) 497333ee039SDag-Erling Smørgrav error("PAM: could not set TZ environment: %s", 498333ee039SDag-Erling Smørgrav strerror(errno)); 49921e764dfSDag-Erling Smørgrav 50021e764dfSDag-Erling Smørgrav if (sshpam_authctxt != NULL) { 50121e764dfSDag-Erling Smørgrav setproctitle("%s [pam]", 50221e764dfSDag-Erling Smørgrav sshpam_authctxt->valid ? pam_user : "unknown"); 50321e764dfSDag-Erling Smørgrav } 504cf2b5f3bSDag-Erling Smørgrav #endif 505cf2b5f3bSDag-Erling Smørgrav 506cf2b5f3bSDag-Erling Smørgrav sshpam_conv.conv = sshpam_thread_conv; 507cf2b5f3bSDag-Erling Smørgrav sshpam_conv.appdata_ptr = ctxt; 508cf2b5f3bSDag-Erling Smørgrav 5095962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 5105962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 5115962c0e9SDag-Erling Smørgrav 512190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) 513190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 514190cef3dSDag-Erling Smørgrav 515cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 516cf2b5f3bSDag-Erling Smørgrav (const void *)&sshpam_conv); 517cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 518cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 51921e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 520076ad2f8SDag-Erling Smørgrav if (sshpam_err == PAM_MAXTRIES) 521076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(1); 522cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 523cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 5241ec0d754SDag-Erling Smørgrav 525333ee039SDag-Erling Smørgrav if (!do_pam_account()) { 526333ee039SDag-Erling Smørgrav sshpam_err = PAM_ACCT_EXPIRED; 5271ec0d754SDag-Erling Smørgrav goto auth_fail; 528333ee039SDag-Erling Smørgrav } 5295962c0e9SDag-Erling Smørgrav if (sshpam_authctxt->force_pwchange) { 5301ec0d754SDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, 5311ec0d754SDag-Erling Smørgrav PAM_CHANGE_EXPIRED_AUTHTOK); 5321ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 5331ec0d754SDag-Erling Smørgrav goto auth_fail; 53421e764dfSDag-Erling Smørgrav sshpam_password_change_required(0); 5351ec0d754SDag-Erling Smørgrav } 5361ec0d754SDag-Erling Smørgrav 537190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, "OK")) != 0) 538190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5391ec0d754SDag-Erling Smørgrav 540aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 5411ec0d754SDag-Erling Smørgrav /* Export variables set by do_pam_account */ 542190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 || 543190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0) 544190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5451ec0d754SDag-Erling Smørgrav 5461ec0d754SDag-Erling Smørgrav /* Export any environment strings set in child */ 547190cef3dSDag-Erling Smørgrav for (i = 0; environ[i] != NULL; i++) { 548190cef3dSDag-Erling Smørgrav /* Count */ 549190cef3dSDag-Erling Smørgrav if (i > INT_MAX) 55019261079SEd Maste fatal("%s: too many environment strings", __func__); 551190cef3dSDag-Erling Smørgrav } 552190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(buffer, i)) != 0) 553190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 554190cef3dSDag-Erling Smørgrav for (i = 0; environ[i] != NULL; i++) { 555190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, environ[i])) != 0) 556190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 557190cef3dSDag-Erling Smørgrav } 5581ec0d754SDag-Erling Smørgrav /* Export any environment strings set by PAM in child */ 5591ec0d754SDag-Erling Smørgrav env_from_pam = pam_getenvlist(sshpam_handle); 560190cef3dSDag-Erling Smørgrav for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { 561190cef3dSDag-Erling Smørgrav /* Count */ 562190cef3dSDag-Erling Smørgrav if (i > INT_MAX) 56319261079SEd Maste fatal("%s: too many PAM environment strings", __func__); 564190cef3dSDag-Erling Smørgrav } 565190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(buffer, i)) != 0) 566190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 567190cef3dSDag-Erling Smørgrav for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { 568190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, env_from_pam[i])) != 0) 569190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 570190cef3dSDag-Erling Smørgrav } 571aa49c926SDag-Erling Smørgrav #endif /* UNSUPPORTED_POSIX_THREADS_HACK */ 5721ec0d754SDag-Erling Smørgrav 5731ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 574190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer); 575190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 576cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 577cf2b5f3bSDag-Erling Smørgrav 578cf2b5f3bSDag-Erling Smørgrav auth_fail: 579190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, 580190cef3dSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err))) != 0) 581190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5821ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 583333ee039SDag-Erling Smørgrav if (sshpam_err == PAM_ACCT_EXPIRED) 584190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer); 585076ad2f8SDag-Erling Smørgrav else if (sshpam_maxtries_reached) 586190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer); 587333ee039SDag-Erling Smørgrav else 588190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, buffer); 589190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 590cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 591cf2b5f3bSDag-Erling Smørgrav 592cf2b5f3bSDag-Erling Smørgrav return (NULL); /* Avoid warning for non-pthread case */ 593cf2b5f3bSDag-Erling Smørgrav } 594cf2b5f3bSDag-Erling Smørgrav 5951ec0d754SDag-Erling Smørgrav void 5961ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void) 597cf2b5f3bSDag-Erling Smørgrav { 5981ec0d754SDag-Erling Smørgrav struct pam_ctxt *ctxt = cleanup_ctxt; 599cf2b5f3bSDag-Erling Smørgrav 6001ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 6011ec0d754SDag-Erling Smørgrav if (ctxt != NULL && ctxt->pam_thread != 0) { 602cf2b5f3bSDag-Erling Smørgrav pthread_cancel(ctxt->pam_thread); 603cf2b5f3bSDag-Erling Smørgrav pthread_join(ctxt->pam_thread, NULL); 604cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_psock); 605cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_csock); 6061ec0d754SDag-Erling Smørgrav memset(ctxt, 0, sizeof(*ctxt)); 6071ec0d754SDag-Erling Smørgrav cleanup_ctxt = NULL; 6081ec0d754SDag-Erling Smørgrav } 609cf2b5f3bSDag-Erling Smørgrav } 610cf2b5f3bSDag-Erling Smørgrav 611cf2b5f3bSDag-Erling Smørgrav static int 612d4ecd108SDag-Erling Smørgrav sshpam_null_conv(int n, sshpam_const struct pam_message **msg, 613cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 614cf2b5f3bSDag-Erling Smørgrav { 6151ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 616cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 617cf2b5f3bSDag-Erling Smørgrav } 618cf2b5f3bSDag-Erling Smørgrav 619cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 620cf2b5f3bSDag-Erling Smørgrav 621aa49c926SDag-Erling Smørgrav static int 622d4ecd108SDag-Erling Smørgrav sshpam_store_conv(int n, sshpam_const struct pam_message **msg, 623aa49c926SDag-Erling Smørgrav struct pam_response **resp, void *data) 624aa49c926SDag-Erling Smørgrav { 625aa49c926SDag-Erling Smørgrav struct pam_response *reply; 626190cef3dSDag-Erling Smørgrav int r, i; 627aa49c926SDag-Erling Smørgrav 628aa49c926SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 629aa49c926SDag-Erling Smørgrav *resp = NULL; 630aa49c926SDag-Erling Smørgrav 631aa49c926SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 632aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 633aa49c926SDag-Erling Smørgrav 634333ee039SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 635aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 636aa49c926SDag-Erling Smørgrav 637aa49c926SDag-Erling Smørgrav for (i = 0; i < n; ++i) { 638aa49c926SDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 639aa49c926SDag-Erling Smørgrav case PAM_ERROR_MSG: 640aa49c926SDag-Erling Smørgrav case PAM_TEXT_INFO: 641190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(loginmsg, "%s\n", 642190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 643190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 644190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 645aa49c926SDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 646aa49c926SDag-Erling Smørgrav break; 647aa49c926SDag-Erling Smørgrav default: 648aa49c926SDag-Erling Smørgrav goto fail; 649aa49c926SDag-Erling Smørgrav } 650aa49c926SDag-Erling Smørgrav } 651aa49c926SDag-Erling Smørgrav *resp = reply; 652aa49c926SDag-Erling Smørgrav return (PAM_SUCCESS); 653aa49c926SDag-Erling Smørgrav 654aa49c926SDag-Erling Smørgrav fail: 655aa49c926SDag-Erling Smørgrav for(i = 0; i < n; i++) { 656e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 657aa49c926SDag-Erling Smørgrav } 658e4a9863fSDag-Erling Smørgrav free(reply); 659aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 660aa49c926SDag-Erling Smørgrav } 661aa49c926SDag-Erling Smørgrav 662aa49c926SDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL }; 663aa49c926SDag-Erling Smørgrav 6641ec0d754SDag-Erling Smørgrav void 6651ec0d754SDag-Erling Smørgrav sshpam_cleanup(void) 666cf2b5f3bSDag-Erling Smørgrav { 667d4af9e69SDag-Erling Smørgrav if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor())) 668cf2b5f3bSDag-Erling Smørgrav return; 669d4af9e69SDag-Erling Smørgrav debug("PAM: cleanup"); 670cf2b5f3bSDag-Erling Smørgrav pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); 671cf2b5f3bSDag-Erling Smørgrav if (sshpam_session_open) { 672d4af9e69SDag-Erling Smørgrav debug("PAM: closing session"); 673cf2b5f3bSDag-Erling Smørgrav pam_close_session(sshpam_handle, PAM_SILENT); 674cf2b5f3bSDag-Erling Smørgrav sshpam_session_open = 0; 675cf2b5f3bSDag-Erling Smørgrav } 6767aee6ffeSDag-Erling Smørgrav if (sshpam_cred_established) { 6777aee6ffeSDag-Erling Smørgrav debug("PAM: deleting credentials"); 6787aee6ffeSDag-Erling Smørgrav pam_setcred(sshpam_handle, PAM_DELETE_CRED); 6797aee6ffeSDag-Erling Smørgrav sshpam_cred_established = 0; 6807aee6ffeSDag-Erling Smørgrav } 6811ec0d754SDag-Erling Smørgrav sshpam_authenticated = 0; 682cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 683cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 684cf2b5f3bSDag-Erling Smørgrav } 685cf2b5f3bSDag-Erling Smørgrav 686cf2b5f3bSDag-Erling Smørgrav static int 68719261079SEd Maste sshpam_init(struct ssh *ssh, Authctxt *authctxt) 688cf2b5f3bSDag-Erling Smørgrav { 68919261079SEd Maste const char *pam_user, *user = authctxt->user; 690d4ecd108SDag-Erling Smørgrav const char **ptr_pam_user = &pam_user; 691*f374ba41SEd Maste int r; 692cf2b5f3bSDag-Erling Smørgrav 69319261079SEd Maste #if defined(PAM_SUN_CODEBASE) && defined(PAM_MAX_RESP_SIZE) 69419261079SEd Maste /* Protect buggy PAM implementations from excessively long usernames */ 69519261079SEd Maste if (strlen(user) >= PAM_MAX_RESP_SIZE) 69619261079SEd Maste fatal("Username too long from %s port %d", 69719261079SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 69819261079SEd Maste #endif 69919261079SEd Maste if (sshpam_handle == NULL) { 70019261079SEd Maste if (ssh == NULL) { 70119261079SEd Maste fatal("%s: called initially with no " 70219261079SEd Maste "packet context", __func__); 70319261079SEd Maste } 70419261079SEd Maste } if (sshpam_handle != NULL) { 705cf2b5f3bSDag-Erling Smørgrav /* We already have a PAM context; check if the user matches */ 706cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_get_item(sshpam_handle, 707d4ecd108SDag-Erling Smørgrav PAM_USER, (sshpam_const void **)ptr_pam_user); 708cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 709cf2b5f3bSDag-Erling Smørgrav return (0); 710cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 711cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 712cf2b5f3bSDag-Erling Smørgrav } 713cf2b5f3bSDag-Erling Smørgrav debug("PAM: initializing for \"%s\"", user); 714cf2b5f3bSDag-Erling Smørgrav sshpam_err = 715aa49c926SDag-Erling Smørgrav pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); 7165962c0e9SDag-Erling Smørgrav sshpam_authctxt = authctxt; 7175962c0e9SDag-Erling Smørgrav 718cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 719cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 720cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 721cf2b5f3bSDag-Erling Smørgrav return (-1); 722cf2b5f3bSDag-Erling Smørgrav } 72319261079SEd Maste 72419261079SEd Maste if (ssh != NULL && sshpam_rhost == NULL) { 72519261079SEd Maste /* 72619261079SEd Maste * We need to cache these as we don't have packet context 72719261079SEd Maste * during the kbdint flow. 72819261079SEd Maste */ 72919261079SEd Maste sshpam_rhost = xstrdup(auth_get_canonical_hostname(ssh, 73019261079SEd Maste options.use_dns)); 73119261079SEd Maste sshpam_laddr = get_local_ipaddr( 73219261079SEd Maste ssh_packet_get_connection_in(ssh)); 73319261079SEd Maste } 73419261079SEd Maste if (sshpam_rhost != NULL) { 73519261079SEd Maste debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost); 73619261079SEd Maste sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, 73719261079SEd Maste sshpam_rhost); 738cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 739cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 740cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 741cf2b5f3bSDag-Erling Smørgrav return (-1); 742cf2b5f3bSDag-Erling Smørgrav } 743*f374ba41SEd Maste } 744*f374ba41SEd Maste if (ssh != NULL && sshpam_laddr != NULL) { 745*f374ba41SEd Maste char *conninfo; 746*f374ba41SEd Maste 74719261079SEd Maste /* Put SSH_CONNECTION in the PAM environment too */ 748*f374ba41SEd Maste xasprintf(&conninfo, "SSH_CONNECTION=%.50s %d %.50s %d", 749*f374ba41SEd Maste ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), 750*f374ba41SEd Maste sshpam_laddr, ssh_local_port(ssh)); 751*f374ba41SEd Maste if ((r = pam_putenv(sshpam_handle, conninfo)) != PAM_SUCCESS) 752*f374ba41SEd Maste logit("pam_putenv: %s", pam_strerror(sshpam_handle, r)); 753*f374ba41SEd Maste free(conninfo); 75419261079SEd Maste } 75519261079SEd Maste 756cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE 757cf2b5f3bSDag-Erling Smørgrav /* 758cf2b5f3bSDag-Erling Smørgrav * Some silly PAM modules (e.g. pam_time) require a TTY to operate. 759cf2b5f3bSDag-Erling Smørgrav * sshd doesn't set the tty until too late in the auth process and 760cf2b5f3bSDag-Erling Smørgrav * may not even set one (for tty-less connections) 761cf2b5f3bSDag-Erling Smørgrav */ 762cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_TTY to \"ssh\""); 763cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); 764cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 765cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 766cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 767cf2b5f3bSDag-Erling Smørgrav return (-1); 768cf2b5f3bSDag-Erling Smørgrav } 769cf2b5f3bSDag-Erling Smørgrav #endif 770cf2b5f3bSDag-Erling Smørgrav return (0); 771cf2b5f3bSDag-Erling Smørgrav } 772cf2b5f3bSDag-Erling Smørgrav 773190cef3dSDag-Erling Smørgrav static void 774190cef3dSDag-Erling Smørgrav expose_authinfo(const char *caller) 775190cef3dSDag-Erling Smørgrav { 776190cef3dSDag-Erling Smørgrav char *auth_info; 777190cef3dSDag-Erling Smørgrav 778190cef3dSDag-Erling Smørgrav /* 779190cef3dSDag-Erling Smørgrav * Expose authentication information to PAM. 780190cef3dSDag-Erling Smørgrav * The environment variable is versioned. Please increment the 781190cef3dSDag-Erling Smørgrav * version suffix if the format of session_info changes. 782190cef3dSDag-Erling Smørgrav */ 783190cef3dSDag-Erling Smørgrav if (sshpam_authctxt->session_info == NULL) 784190cef3dSDag-Erling Smørgrav auth_info = xstrdup(""); 785190cef3dSDag-Erling Smørgrav else if ((auth_info = sshbuf_dup_string( 786190cef3dSDag-Erling Smørgrav sshpam_authctxt->session_info)) == NULL) 787190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_dup_string failed", __func__); 788190cef3dSDag-Erling Smørgrav 789190cef3dSDag-Erling Smørgrav debug2("%s: auth information in SSH_AUTH_INFO_0", caller); 790190cef3dSDag-Erling Smørgrav do_pam_putenv("SSH_AUTH_INFO_0", auth_info); 791190cef3dSDag-Erling Smørgrav free(auth_info); 792190cef3dSDag-Erling Smørgrav } 793190cef3dSDag-Erling Smørgrav 794cf2b5f3bSDag-Erling Smørgrav static void * 795cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt) 796cf2b5f3bSDag-Erling Smørgrav { 797cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 79819261079SEd Maste int result, socks[2]; 799cf2b5f3bSDag-Erling Smørgrav 8001ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 801333ee039SDag-Erling Smørgrav /* 802333ee039SDag-Erling Smørgrav * Refuse to start if we don't have PAM enabled or do_pam_account 803333ee039SDag-Erling Smørgrav * has previously failed. 804333ee039SDag-Erling Smørgrav */ 805333ee039SDag-Erling Smørgrav if (!options.use_pam || sshpam_account_status == 0) 806cf2b5f3bSDag-Erling Smørgrav return NULL; 807cf2b5f3bSDag-Erling Smørgrav 808cf2b5f3bSDag-Erling Smørgrav /* Initialize PAM */ 80919261079SEd Maste if (sshpam_init(NULL, authctxt) == -1) { 810cf2b5f3bSDag-Erling Smørgrav error("PAM: initialization failed"); 811cf2b5f3bSDag-Erling Smørgrav return (NULL); 812cf2b5f3bSDag-Erling Smørgrav } 813cf2b5f3bSDag-Erling Smørgrav 814190cef3dSDag-Erling Smørgrav expose_authinfo(__func__); 815d4af9e69SDag-Erling Smørgrav ctxt = xcalloc(1, sizeof *ctxt); 8161ec0d754SDag-Erling Smørgrav 817cf2b5f3bSDag-Erling Smørgrav /* Start the authentication thread */ 818cf2b5f3bSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 819cf2b5f3bSDag-Erling Smørgrav error("PAM: failed create sockets: %s", strerror(errno)); 820e4a9863fSDag-Erling Smørgrav free(ctxt); 821cf2b5f3bSDag-Erling Smørgrav return (NULL); 822cf2b5f3bSDag-Erling Smørgrav } 823cf2b5f3bSDag-Erling Smørgrav ctxt->pam_psock = socks[0]; 824cf2b5f3bSDag-Erling Smørgrav ctxt->pam_csock = socks[1]; 82519261079SEd Maste result = pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt); 82619261079SEd Maste if (result != 0) { 827cf2b5f3bSDag-Erling Smørgrav error("PAM: failed to start authentication thread: %s", 82819261079SEd Maste strerror(result)); 829cf2b5f3bSDag-Erling Smørgrav close(socks[0]); 830cf2b5f3bSDag-Erling Smørgrav close(socks[1]); 831e4a9863fSDag-Erling Smørgrav free(ctxt); 832cf2b5f3bSDag-Erling Smørgrav return (NULL); 833cf2b5f3bSDag-Erling Smørgrav } 8341ec0d754SDag-Erling Smørgrav cleanup_ctxt = ctxt; 835cf2b5f3bSDag-Erling Smørgrav return (ctxt); 836cf2b5f3bSDag-Erling Smørgrav } 837cf2b5f3bSDag-Erling Smørgrav 838cf2b5f3bSDag-Erling Smørgrav static int 839cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info, 840cf2b5f3bSDag-Erling Smørgrav u_int *num, char ***prompts, u_int **echo_on) 841cf2b5f3bSDag-Erling Smørgrav { 842190cef3dSDag-Erling Smørgrav struct sshbuf *buffer; 843cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 844cf2b5f3bSDag-Erling Smørgrav size_t plen; 845cf2b5f3bSDag-Erling Smørgrav u_char type; 846cf2b5f3bSDag-Erling Smørgrav char *msg; 847aa49c926SDag-Erling Smørgrav size_t len, mlen; 848190cef3dSDag-Erling Smørgrav int r; 849cf2b5f3bSDag-Erling Smørgrav 8501ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 851190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) 852190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 853cf2b5f3bSDag-Erling Smørgrav *name = xstrdup(""); 854cf2b5f3bSDag-Erling Smørgrav *info = xstrdup(""); 855cf2b5f3bSDag-Erling Smørgrav *prompts = xmalloc(sizeof(char *)); 856cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 857cf2b5f3bSDag-Erling Smørgrav plen = 0; 858cf2b5f3bSDag-Erling Smørgrav *echo_on = xmalloc(sizeof(u_int)); 859190cef3dSDag-Erling Smørgrav while (ssh_msg_recv(ctxt->pam_psock, buffer) == 0) { 860190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u8(buffer, &type)) != 0 || 861190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(buffer, &msg, &mlen)) != 0) 862190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 863cf2b5f3bSDag-Erling Smørgrav switch (type) { 864cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 865cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 866cf2b5f3bSDag-Erling Smørgrav *num = 1; 867aa49c926SDag-Erling Smørgrav len = plen + mlen + 1; 868557f75e5SDag-Erling Smørgrav **prompts = xreallocarray(**prompts, 1, len); 869aa49c926SDag-Erling Smørgrav strlcpy(**prompts + plen, msg, len - plen); 870aa49c926SDag-Erling Smørgrav plen += mlen; 871cf2b5f3bSDag-Erling Smørgrav **echo_on = (type == PAM_PROMPT_ECHO_ON); 872e4a9863fSDag-Erling Smørgrav free(msg); 87319261079SEd Maste sshbuf_free(buffer); 874cf2b5f3bSDag-Erling Smørgrav return (0); 875cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 876cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 877cf2b5f3bSDag-Erling Smørgrav /* accumulate messages */ 878aa49c926SDag-Erling Smørgrav len = plen + mlen + 2; 879557f75e5SDag-Erling Smørgrav **prompts = xreallocarray(**prompts, 1, len); 880aa49c926SDag-Erling Smørgrav strlcpy(**prompts + plen, msg, len - plen); 881aa49c926SDag-Erling Smørgrav plen += mlen; 882aa49c926SDag-Erling Smørgrav strlcat(**prompts + plen, "\n", len - plen); 883aa49c926SDag-Erling Smørgrav plen++; 884e4a9863fSDag-Erling Smørgrav free(msg); 885cf2b5f3bSDag-Erling Smørgrav break; 886333ee039SDag-Erling Smørgrav case PAM_ACCT_EXPIRED: 887076ad2f8SDag-Erling Smørgrav case PAM_MAXTRIES: 888076ad2f8SDag-Erling Smørgrav if (type == PAM_ACCT_EXPIRED) 889333ee039SDag-Erling Smørgrav sshpam_account_status = 0; 890076ad2f8SDag-Erling Smørgrav if (type == PAM_MAXTRIES) 891076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(1); 892333ee039SDag-Erling Smørgrav /* FALLTHROUGH */ 893cf2b5f3bSDag-Erling Smørgrav case PAM_AUTH_ERR: 894333ee039SDag-Erling Smørgrav debug3("PAM: %s", pam_strerror(sshpam_handle, type)); 895b74df5b2SDag-Erling Smørgrav if (**prompts != NULL && strlen(**prompts) != 0) { 896e9e8876aSEd Maste free(*info); 897b74df5b2SDag-Erling Smørgrav *info = **prompts; 898b74df5b2SDag-Erling Smørgrav **prompts = NULL; 899b74df5b2SDag-Erling Smørgrav *num = 0; 900b74df5b2SDag-Erling Smørgrav **echo_on = 0; 901b74df5b2SDag-Erling Smørgrav ctxt->pam_done = -1; 902e4a9863fSDag-Erling Smørgrav free(msg); 90319261079SEd Maste sshbuf_free(buffer); 904b74df5b2SDag-Erling Smørgrav return 0; 905b74df5b2SDag-Erling Smørgrav } 906b74df5b2SDag-Erling Smørgrav /* FALLTHROUGH */ 907b74df5b2SDag-Erling Smørgrav case PAM_SUCCESS: 908cf2b5f3bSDag-Erling Smørgrav if (**prompts != NULL) { 909cf2b5f3bSDag-Erling Smørgrav /* drain any accumulated messages */ 9101ec0d754SDag-Erling Smørgrav debug("PAM: %s", **prompts); 911190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(loginmsg, **prompts, 912190cef3dSDag-Erling Smørgrav strlen(**prompts))) != 0) 913190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 914190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 915e4a9863fSDag-Erling Smørgrav free(**prompts); 916cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 917cf2b5f3bSDag-Erling Smørgrav } 918cf2b5f3bSDag-Erling Smørgrav if (type == PAM_SUCCESS) { 919aa49c926SDag-Erling Smørgrav if (!sshpam_authctxt->valid || 920aa49c926SDag-Erling Smørgrav (sshpam_authctxt->pw->pw_uid == 0 && 921aa49c926SDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 922aa49c926SDag-Erling Smørgrav fatal("Internal error: PAM auth " 923aa49c926SDag-Erling Smørgrav "succeeded when it should have " 924aa49c926SDag-Erling Smørgrav "failed"); 925190cef3dSDag-Erling Smørgrav import_environments(buffer); 926cf2b5f3bSDag-Erling Smørgrav *num = 0; 927cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 928cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = 1; 929e4a9863fSDag-Erling Smørgrav free(msg); 93019261079SEd Maste sshbuf_free(buffer); 931cf2b5f3bSDag-Erling Smørgrav return (0); 932cf2b5f3bSDag-Erling Smørgrav } 9330f9bafdfSEd Maste BLACKLIST_NOTIFY(NULL, BLACKLIST_BAD_USER, 934342b8b88SKurt Lidl sshpam_authctxt->user); 9355962c0e9SDag-Erling Smørgrav error("PAM: %s for %s%.100s from %.100s", msg, 9365962c0e9SDag-Erling Smørgrav sshpam_authctxt->valid ? "" : "illegal user ", 93719261079SEd Maste sshpam_authctxt->user, sshpam_rhost); 9381ec0d754SDag-Erling Smørgrav /* FALLTHROUGH */ 939cf2b5f3bSDag-Erling Smørgrav default: 940cf2b5f3bSDag-Erling Smørgrav *num = 0; 941cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 942e4a9863fSDag-Erling Smørgrav free(msg); 943cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = -1; 94419261079SEd Maste sshbuf_free(buffer); 945cf2b5f3bSDag-Erling Smørgrav return (-1); 946cf2b5f3bSDag-Erling Smørgrav } 947cf2b5f3bSDag-Erling Smørgrav } 94819261079SEd Maste sshbuf_free(buffer); 949cf2b5f3bSDag-Erling Smørgrav return (-1); 950cf2b5f3bSDag-Erling Smørgrav } 951cf2b5f3bSDag-Erling Smørgrav 952076ad2f8SDag-Erling Smørgrav /* 953076ad2f8SDag-Erling Smørgrav * Returns a junk password of identical length to that the user supplied. 954076ad2f8SDag-Erling Smørgrav * Used to mitigate timing attacks against crypt(3)/PAM stacks that 955076ad2f8SDag-Erling Smørgrav * vary processing time in proportion to password length. 956076ad2f8SDag-Erling Smørgrav */ 957076ad2f8SDag-Erling Smørgrav static char * 958076ad2f8SDag-Erling Smørgrav fake_password(const char *wire_password) 959076ad2f8SDag-Erling Smørgrav { 960076ad2f8SDag-Erling Smørgrav const char junk[] = "\b\n\r\177INCORRECT"; 961076ad2f8SDag-Erling Smørgrav char *ret = NULL; 962076ad2f8SDag-Erling Smørgrav size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; 963076ad2f8SDag-Erling Smørgrav 964076ad2f8SDag-Erling Smørgrav if (l >= INT_MAX) 965076ad2f8SDag-Erling Smørgrav fatal("%s: password length too long: %zu", __func__, l); 966076ad2f8SDag-Erling Smørgrav 967076ad2f8SDag-Erling Smørgrav ret = malloc(l + 1); 968d93a896eSDag-Erling Smørgrav if (ret == NULL) 969d93a896eSDag-Erling Smørgrav return NULL; 970076ad2f8SDag-Erling Smørgrav for (i = 0; i < l; i++) 971076ad2f8SDag-Erling Smørgrav ret[i] = junk[i % (sizeof(junk) - 1)]; 972076ad2f8SDag-Erling Smørgrav ret[i] = '\0'; 973076ad2f8SDag-Erling Smørgrav return ret; 974076ad2f8SDag-Erling Smørgrav } 975076ad2f8SDag-Erling Smørgrav 976cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */ 977cf2b5f3bSDag-Erling Smørgrav static int 978cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp) 979cf2b5f3bSDag-Erling Smørgrav { 980190cef3dSDag-Erling Smørgrav struct sshbuf *buffer; 981cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 982076ad2f8SDag-Erling Smørgrav char *fake; 983190cef3dSDag-Erling Smørgrav int r; 984cf2b5f3bSDag-Erling Smørgrav 985b74df5b2SDag-Erling Smørgrav debug2("PAM: %s entering, %u responses", __func__, num); 986cf2b5f3bSDag-Erling Smørgrav switch (ctxt->pam_done) { 987cf2b5f3bSDag-Erling Smørgrav case 1: 988cf2b5f3bSDag-Erling Smørgrav sshpam_authenticated = 1; 989cf2b5f3bSDag-Erling Smørgrav return (0); 990cf2b5f3bSDag-Erling Smørgrav case 0: 991cf2b5f3bSDag-Erling Smørgrav break; 992cf2b5f3bSDag-Erling Smørgrav default: 993cf2b5f3bSDag-Erling Smørgrav return (-1); 994cf2b5f3bSDag-Erling Smørgrav } 995cf2b5f3bSDag-Erling Smørgrav if (num != 1) { 996cf2b5f3bSDag-Erling Smørgrav error("PAM: expected one response, got %u", num); 997cf2b5f3bSDag-Erling Smørgrav return (-1); 998cf2b5f3bSDag-Erling Smørgrav } 999190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) 1000190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1001aa49c926SDag-Erling Smørgrav if (sshpam_authctxt->valid && 1002aa49c926SDag-Erling Smørgrav (sshpam_authctxt->pw->pw_uid != 0 || 1003190cef3dSDag-Erling Smørgrav options.permit_root_login == PERMIT_YES)) { 1004190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, *resp)) != 0) 1005190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1006190cef3dSDag-Erling Smørgrav } else { 1007076ad2f8SDag-Erling Smørgrav fake = fake_password(*resp); 1008190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, fake)) != 0) 1009190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1010076ad2f8SDag-Erling Smørgrav free(fake); 1011076ad2f8SDag-Erling Smørgrav } 1012190cef3dSDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, buffer) == -1) { 1013190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 10141ec0d754SDag-Erling Smørgrav return (-1); 10151ec0d754SDag-Erling Smørgrav } 1016190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 1017cf2b5f3bSDag-Erling Smørgrav return (1); 1018cf2b5f3bSDag-Erling Smørgrav } 1019cf2b5f3bSDag-Erling Smørgrav 1020cf2b5f3bSDag-Erling Smørgrav static void 1021cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp) 1022cf2b5f3bSDag-Erling Smørgrav { 1023cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 1024cf2b5f3bSDag-Erling Smørgrav 10251ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 10261ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(); 1027e4a9863fSDag-Erling Smørgrav free(ctxt); 1028cf2b5f3bSDag-Erling Smørgrav /* 1029cf2b5f3bSDag-Erling Smørgrav * We don't call sshpam_cleanup() here because we may need the PAM 1030cf2b5f3bSDag-Erling Smørgrav * handle at a later stage, e.g. when setting up a session. It's 1031cf2b5f3bSDag-Erling Smørgrav * still on the cleanup list, so pam_end() *will* be called before 1032cf2b5f3bSDag-Erling Smørgrav * the server process terminates. 1033cf2b5f3bSDag-Erling Smørgrav */ 1034cf2b5f3bSDag-Erling Smørgrav } 1035cf2b5f3bSDag-Erling Smørgrav 1036cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = { 1037cf2b5f3bSDag-Erling Smørgrav "pam", 1038cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx, 1039cf2b5f3bSDag-Erling Smørgrav sshpam_query, 1040cf2b5f3bSDag-Erling Smørgrav sshpam_respond, 1041cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx 1042cf2b5f3bSDag-Erling Smørgrav }; 1043cf2b5f3bSDag-Erling Smørgrav 1044cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = { 1045cf2b5f3bSDag-Erling Smørgrav "pam", 1046cf2b5f3bSDag-Erling Smørgrav mm_sshpam_init_ctx, 1047cf2b5f3bSDag-Erling Smørgrav mm_sshpam_query, 1048cf2b5f3bSDag-Erling Smørgrav mm_sshpam_respond, 1049cf2b5f3bSDag-Erling Smørgrav mm_sshpam_free_ctx 1050cf2b5f3bSDag-Erling Smørgrav }; 1051cf2b5f3bSDag-Erling Smørgrav 1052cf2b5f3bSDag-Erling Smørgrav /* 1053cf2b5f3bSDag-Erling Smørgrav * This replaces auth-pam.c 1054cf2b5f3bSDag-Erling Smørgrav */ 1055cf2b5f3bSDag-Erling Smørgrav void 105619261079SEd Maste start_pam(struct ssh *ssh) 1057cf2b5f3bSDag-Erling Smørgrav { 105819261079SEd Maste Authctxt *authctxt = (Authctxt *)ssh->authctxt; 105919261079SEd Maste 1060cf2b5f3bSDag-Erling Smørgrav if (!options.use_pam) 1061cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation requested when UsePAM=no"); 1062cf2b5f3bSDag-Erling Smørgrav 106319261079SEd Maste if (sshpam_init(ssh, authctxt) == -1) 1064cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation failed"); 1065cf2b5f3bSDag-Erling Smørgrav } 1066cf2b5f3bSDag-Erling Smørgrav 1067cf2b5f3bSDag-Erling Smørgrav void 1068cf2b5f3bSDag-Erling Smørgrav finish_pam(void) 1069cf2b5f3bSDag-Erling Smørgrav { 10701ec0d754SDag-Erling Smørgrav sshpam_cleanup(); 1071cf2b5f3bSDag-Erling Smørgrav } 1072cf2b5f3bSDag-Erling Smørgrav 10734f52dfbbSDag-Erling Smørgrav 1074cf2b5f3bSDag-Erling Smørgrav u_int 1075cf2b5f3bSDag-Erling Smørgrav do_pam_account(void) 1076cf2b5f3bSDag-Erling Smørgrav { 1077aa49c926SDag-Erling Smørgrav debug("%s: called", __func__); 10781ec0d754SDag-Erling Smørgrav if (sshpam_account_status != -1) 10791ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 10801ec0d754SDag-Erling Smørgrav 10814f52dfbbSDag-Erling Smørgrav expose_authinfo(__func__); 10824f52dfbbSDag-Erling Smørgrav 1083cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_acct_mgmt(sshpam_handle, 0); 1084aa49c926SDag-Erling Smørgrav debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, 1085aa49c926SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1086cf2b5f3bSDag-Erling Smørgrav 10871ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { 10881ec0d754SDag-Erling Smørgrav sshpam_account_status = 0; 10891ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 109009958426SBrian Feldman } 109109958426SBrian Feldman 10921ec0d754SDag-Erling Smørgrav if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 109321e764dfSDag-Erling Smørgrav sshpam_password_change_required(1); 109409958426SBrian Feldman 10951ec0d754SDag-Erling Smørgrav sshpam_account_status = 1; 10961ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 109709958426SBrian Feldman } 109809958426SBrian Feldman 1099cf2b5f3bSDag-Erling Smørgrav void 1100cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init) 110109958426SBrian Feldman { 1102cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1103aa49c926SDag-Erling Smørgrav (const void *)&store_conv); 1104cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 1105cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 1106cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1107cf2b5f3bSDag-Erling Smørgrav if (init) { 1108cf2b5f3bSDag-Erling Smørgrav debug("PAM: establishing credentials"); 1109cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); 1110cf2b5f3bSDag-Erling Smørgrav } else { 1111cf2b5f3bSDag-Erling Smørgrav debug("PAM: reinitializing credentials"); 1112cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); 1113cf2b5f3bSDag-Erling Smørgrav } 1114cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) { 1115cf2b5f3bSDag-Erling Smørgrav sshpam_cred_established = 1; 1116989dd127SDag-Erling Smørgrav return; 1117cf2b5f3bSDag-Erling Smørgrav } 1118cf2b5f3bSDag-Erling Smørgrav if (sshpam_authenticated) 1119cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_setcred(): %s", 1120cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 11212c917d39SAlfred Perlstein else 1122cf2b5f3bSDag-Erling Smørgrav debug("PAM: pam_setcred(): %s", 1123cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 112409958426SBrian Feldman } 112509958426SBrian Feldman 1126cf2b5f3bSDag-Erling Smørgrav static int 1127d4ecd108SDag-Erling Smørgrav sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, 1128cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 112909958426SBrian Feldman { 1130cf2b5f3bSDag-Erling Smørgrav char input[PAM_MAX_MSG_SIZE]; 1131cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 1132f388f5efSDag-Erling Smørgrav int i; 1133f388f5efSDag-Erling Smørgrav 11341ec0d754SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 11351ec0d754SDag-Erling Smørgrav 1136cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 1137cf2b5f3bSDag-Erling Smørgrav 11381ec0d754SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) 1139cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 1140cf2b5f3bSDag-Erling Smørgrav 1141333ee039SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 1142cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 1143cf2b5f3bSDag-Erling Smørgrav 1144cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 1145cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 1146cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 1147cf2b5f3bSDag-Erling Smørgrav reply[i].resp = 1148cf2b5f3bSDag-Erling Smørgrav read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 1149cf2b5f3bSDag-Erling Smørgrav RP_ALLOW_STDIN); 1150cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 1151cf2b5f3bSDag-Erling Smørgrav break; 1152cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 11531ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 1154d4af9e69SDag-Erling Smørgrav if (fgets(input, sizeof input, stdin) == NULL) 1155d4af9e69SDag-Erling Smørgrav input[0] = '\0'; 115621e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(input)) == NULL) 115721e764dfSDag-Erling Smørgrav goto fail; 1158cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 1159cf2b5f3bSDag-Erling Smørgrav break; 1160cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 1161cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 11621ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 1163cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 1164cf2b5f3bSDag-Erling Smørgrav break; 1165cf2b5f3bSDag-Erling Smørgrav default: 1166cf2b5f3bSDag-Erling Smørgrav goto fail; 1167f388f5efSDag-Erling Smørgrav } 1168f388f5efSDag-Erling Smørgrav } 1169cf2b5f3bSDag-Erling Smørgrav *resp = reply; 1170cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 1171cf2b5f3bSDag-Erling Smørgrav 1172cf2b5f3bSDag-Erling Smørgrav fail: 1173cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 1174e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 1175cf2b5f3bSDag-Erling Smørgrav } 1176e4a9863fSDag-Erling Smørgrav free(reply); 1177cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 1178cf2b5f3bSDag-Erling Smørgrav } 1179f388f5efSDag-Erling Smørgrav 118021e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; 11811ec0d754SDag-Erling Smørgrav 1182cf2b5f3bSDag-Erling Smørgrav /* 1183cf2b5f3bSDag-Erling Smørgrav * XXX this should be done in the authentication phase, but ssh1 doesn't 1184cf2b5f3bSDag-Erling Smørgrav * support that 1185cf2b5f3bSDag-Erling Smørgrav */ 1186cf2b5f3bSDag-Erling Smørgrav void 1187cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void) 118809958426SBrian Feldman { 1189cf2b5f3bSDag-Erling Smørgrav if (use_privsep) 1190cf2b5f3bSDag-Erling Smørgrav fatal("Password expired (unable to change with privsep)"); 1191cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 11921ec0d754SDag-Erling Smørgrav (const void *)&tty_conv); 1193cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 1194cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 1195cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1196cf2b5f3bSDag-Erling Smørgrav debug("PAM: changing password"); 1197cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); 1198cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 1199cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_chauthtok(): %s", 1200cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 120109958426SBrian Feldman } 120209958426SBrian Feldman 12031ec0d754SDag-Erling Smørgrav void 120447dd1d1bSDag-Erling Smørgrav do_pam_session(struct ssh *ssh) 12051ec0d754SDag-Erling Smørgrav { 12061ec0d754SDag-Erling Smørgrav debug3("PAM: opening session"); 12074f52dfbbSDag-Erling Smørgrav 12084f52dfbbSDag-Erling Smørgrav expose_authinfo(__func__); 12094f52dfbbSDag-Erling Smørgrav 12101ec0d754SDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 12111ec0d754SDag-Erling Smørgrav (const void *)&store_conv); 12121ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 12131ec0d754SDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 12141ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 12151ec0d754SDag-Erling Smørgrav sshpam_err = pam_open_session(sshpam_handle, 0); 1216aa49c926SDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) 12171ec0d754SDag-Erling Smørgrav sshpam_session_open = 1; 1218aa49c926SDag-Erling Smørgrav else { 1219aa49c926SDag-Erling Smørgrav sshpam_session_open = 0; 122047dd1d1bSDag-Erling Smørgrav auth_restrict_session(ssh); 1221aa49c926SDag-Erling Smørgrav error("PAM: pam_open_session(): %s", 1222aa49c926SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1223aa49c926SDag-Erling Smørgrav } 1224aa49c926SDag-Erling Smørgrav 1225aa49c926SDag-Erling Smørgrav } 1226aa49c926SDag-Erling Smørgrav 1227aa49c926SDag-Erling Smørgrav int 1228aa49c926SDag-Erling Smørgrav is_pam_session_open(void) 1229aa49c926SDag-Erling Smørgrav { 1230aa49c926SDag-Erling Smørgrav return sshpam_session_open; 12311ec0d754SDag-Erling Smørgrav } 12321ec0d754SDag-Erling Smørgrav 1233cf2b5f3bSDag-Erling Smørgrav /* 1234cf2b5f3bSDag-Erling Smørgrav * Set a PAM environment string. We need to do this so that the session 1235cf2b5f3bSDag-Erling Smørgrav * modules can handle things like Kerberos/GSI credentials that appear 1236cf2b5f3bSDag-Erling Smørgrav * during the ssh authentication process. 1237cf2b5f3bSDag-Erling Smørgrav */ 1238cf2b5f3bSDag-Erling Smørgrav int 1239cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value) 124009958426SBrian Feldman { 1241cf2b5f3bSDag-Erling Smørgrav int ret = 1; 1242cf2b5f3bSDag-Erling Smørgrav char *compound; 1243cf2b5f3bSDag-Erling Smørgrav size_t len; 124409958426SBrian Feldman 1245cf2b5f3bSDag-Erling Smørgrav len = strlen(name) + strlen(value) + 2; 1246cf2b5f3bSDag-Erling Smørgrav compound = xmalloc(len); 124709958426SBrian Feldman 1248cf2b5f3bSDag-Erling Smørgrav snprintf(compound, len, "%s=%s", name, value); 1249cf2b5f3bSDag-Erling Smørgrav ret = pam_putenv(sshpam_handle, compound); 1250e4a9863fSDag-Erling Smørgrav free(compound); 125109958426SBrian Feldman 1252cf2b5f3bSDag-Erling Smørgrav return (ret); 1253cf2b5f3bSDag-Erling Smørgrav } 125409958426SBrian Feldman 12551ec0d754SDag-Erling Smørgrav char ** 12561ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void) 1257cf2b5f3bSDag-Erling Smørgrav { 12581ec0d754SDag-Erling Smørgrav return sshpam_env; 1259cf2b5f3bSDag-Erling Smørgrav } 1260cf2b5f3bSDag-Erling Smørgrav 1261cf2b5f3bSDag-Erling Smørgrav char ** 1262cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void) 1263cf2b5f3bSDag-Erling Smørgrav { 1264cf2b5f3bSDag-Erling Smørgrav return (pam_getenvlist(sshpam_handle)); 1265cf2b5f3bSDag-Erling Smørgrav } 1266cf2b5f3bSDag-Erling Smørgrav 1267cf2b5f3bSDag-Erling Smørgrav void 1268cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env) 1269cf2b5f3bSDag-Erling Smørgrav { 1270cf2b5f3bSDag-Erling Smørgrav char **envp; 1271cf2b5f3bSDag-Erling Smørgrav 1272cf2b5f3bSDag-Erling Smørgrav if (env == NULL) 1273cf2b5f3bSDag-Erling Smørgrav return; 1274cf2b5f3bSDag-Erling Smørgrav 1275cf2b5f3bSDag-Erling Smørgrav for (envp = env; *envp; envp++) 1276e4a9863fSDag-Erling Smørgrav free(*envp); 1277e4a9863fSDag-Erling Smørgrav free(env); 127809958426SBrian Feldman } 127909958426SBrian Feldman 128021e764dfSDag-Erling Smørgrav /* 128121e764dfSDag-Erling Smørgrav * "Blind" conversation function for password authentication. Assumes that 128221e764dfSDag-Erling Smørgrav * echo-off prompts are for the password and stores messages for later 128321e764dfSDag-Erling Smørgrav * display. 128421e764dfSDag-Erling Smørgrav */ 128521e764dfSDag-Erling Smørgrav static int 1286d4ecd108SDag-Erling Smørgrav sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, 128721e764dfSDag-Erling Smørgrav struct pam_response **resp, void *data) 128821e764dfSDag-Erling Smørgrav { 128921e764dfSDag-Erling Smørgrav struct pam_response *reply; 1290190cef3dSDag-Erling Smørgrav int r, i; 129121e764dfSDag-Erling Smørgrav size_t len; 129221e764dfSDag-Erling Smørgrav 129321e764dfSDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 129421e764dfSDag-Erling Smørgrav 129521e764dfSDag-Erling Smørgrav *resp = NULL; 129621e764dfSDag-Erling Smørgrav 129721e764dfSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 129821e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 129921e764dfSDag-Erling Smørgrav 1300d4af9e69SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 130121e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 130221e764dfSDag-Erling Smørgrav 130321e764dfSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 130421e764dfSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 130521e764dfSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 130621e764dfSDag-Erling Smørgrav if (sshpam_password == NULL) 130721e764dfSDag-Erling Smørgrav goto fail; 130821e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(sshpam_password)) == NULL) 130921e764dfSDag-Erling Smørgrav goto fail; 131021e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 131121e764dfSDag-Erling Smørgrav break; 131221e764dfSDag-Erling Smørgrav case PAM_ERROR_MSG: 131321e764dfSDag-Erling Smørgrav case PAM_TEXT_INFO: 131421e764dfSDag-Erling Smørgrav len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 131521e764dfSDag-Erling Smørgrav if (len > 0) { 1316190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(loginmsg, "%s\n", 1317190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 1318190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1319190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 132021e764dfSDag-Erling Smørgrav } 132121e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup("")) == NULL) 132221e764dfSDag-Erling Smørgrav goto fail; 132321e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 132421e764dfSDag-Erling Smørgrav break; 132521e764dfSDag-Erling Smørgrav default: 132621e764dfSDag-Erling Smørgrav goto fail; 132721e764dfSDag-Erling Smørgrav } 132821e764dfSDag-Erling Smørgrav } 132921e764dfSDag-Erling Smørgrav *resp = reply; 133021e764dfSDag-Erling Smørgrav return (PAM_SUCCESS); 133121e764dfSDag-Erling Smørgrav 133221e764dfSDag-Erling Smørgrav fail: 133321e764dfSDag-Erling Smørgrav for(i = 0; i < n; i++) { 1334e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 133521e764dfSDag-Erling Smørgrav } 1336e4a9863fSDag-Erling Smørgrav free(reply); 133721e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 133821e764dfSDag-Erling Smørgrav } 133921e764dfSDag-Erling Smørgrav 134021e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; 134121e764dfSDag-Erling Smørgrav 134221e764dfSDag-Erling Smørgrav /* 134321e764dfSDag-Erling Smørgrav * Attempt password authentication via PAM 134421e764dfSDag-Erling Smørgrav */ 134521e764dfSDag-Erling Smørgrav int 134621e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password) 134721e764dfSDag-Erling Smørgrav { 134821e764dfSDag-Erling Smørgrav int flags = (options.permit_empty_passwd == 0 ? 134921e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 1350076ad2f8SDag-Erling Smørgrav char *fake = NULL; 135121e764dfSDag-Erling Smørgrav 135221e764dfSDag-Erling Smørgrav if (!options.use_pam || sshpam_handle == NULL) 135321e764dfSDag-Erling Smørgrav fatal("PAM: %s called when PAM disabled or failed to " 135421e764dfSDag-Erling Smørgrav "initialise.", __func__); 135521e764dfSDag-Erling Smørgrav 135621e764dfSDag-Erling Smørgrav sshpam_password = password; 135721e764dfSDag-Erling Smørgrav sshpam_authctxt = authctxt; 135821e764dfSDag-Erling Smørgrav 135921e764dfSDag-Erling Smørgrav /* 136021e764dfSDag-Erling Smørgrav * If the user logging in is invalid, or is root but is not permitted 136121e764dfSDag-Erling Smørgrav * by PermitRootLogin, use an invalid password to prevent leaking 136221e764dfSDag-Erling Smørgrav * information via timing (eg if the PAM config has a delay on fail). 136321e764dfSDag-Erling Smørgrav */ 136421e764dfSDag-Erling Smørgrav if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && 136521e764dfSDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 1366076ad2f8SDag-Erling Smørgrav sshpam_password = fake = fake_password(password); 136721e764dfSDag-Erling Smørgrav 136821e764dfSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 136921e764dfSDag-Erling Smørgrav (const void *)&passwd_conv); 137021e764dfSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 137121e764dfSDag-Erling Smørgrav fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, 137221e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 137321e764dfSDag-Erling Smørgrav 137421e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 137521e764dfSDag-Erling Smørgrav sshpam_password = NULL; 1376076ad2f8SDag-Erling Smørgrav free(fake); 1377076ad2f8SDag-Erling Smørgrav if (sshpam_err == PAM_MAXTRIES) 1378076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(1); 137921e764dfSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && authctxt->valid) { 138021e764dfSDag-Erling Smørgrav debug("PAM: password authentication accepted for %.100s", 138121e764dfSDag-Erling Smørgrav authctxt->user); 138221e764dfSDag-Erling Smørgrav return 1; 138321e764dfSDag-Erling Smørgrav } else { 138421e764dfSDag-Erling Smørgrav debug("PAM: password authentication failed for %.100s: %s", 138521e764dfSDag-Erling Smørgrav authctxt->valid ? authctxt->user : "an illegal user", 138621e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 138721e764dfSDag-Erling Smørgrav return 0; 138821e764dfSDag-Erling Smørgrav } 138921e764dfSDag-Erling Smørgrav } 1390076ad2f8SDag-Erling Smørgrav 1391076ad2f8SDag-Erling Smørgrav int 1392076ad2f8SDag-Erling Smørgrav sshpam_get_maxtries_reached(void) 1393076ad2f8SDag-Erling Smørgrav { 1394076ad2f8SDag-Erling Smørgrav return sshpam_maxtries_reached; 1395076ad2f8SDag-Erling Smørgrav } 1396076ad2f8SDag-Erling Smørgrav 1397076ad2f8SDag-Erling Smørgrav void 1398076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(int reached) 1399076ad2f8SDag-Erling Smørgrav { 1400076ad2f8SDag-Erling Smørgrav if (reached == 0 || sshpam_maxtries_reached) 1401076ad2f8SDag-Erling Smørgrav return; 1402076ad2f8SDag-Erling Smørgrav sshpam_maxtries_reached = 1; 1403076ad2f8SDag-Erling Smørgrav options.password_authentication = 0; 1404076ad2f8SDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1405076ad2f8SDag-Erling Smørgrav } 140609958426SBrian Feldman #endif /* USE_PAM */ 1407