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> 59333ee039SDag-Erling Smørgrav #include <string.h> 60333ee039SDag-Erling Smørgrav #include <unistd.h> 6109958426SBrian Feldman 6209958426SBrian Feldman #ifdef USE_PAM 631ec0d754SDag-Erling Smørgrav #if defined(HAVE_SECURITY_PAM_APPL_H) 64cf2b5f3bSDag-Erling Smørgrav #include <security/pam_appl.h> 651ec0d754SDag-Erling Smørgrav #elif defined (HAVE_PAM_PAM_APPL_H) 661ec0d754SDag-Erling Smørgrav #include <pam/pam_appl.h> 671ec0d754SDag-Erling Smørgrav #endif 68cf2b5f3bSDag-Erling Smørgrav 69ca86bcf2SDag-Erling Smørgrav #if !defined(SSHD_PAM_SERVICE) 70ca86bcf2SDag-Erling Smørgrav extern char *__progname; 71ca86bcf2SDag-Erling Smørgrav # define SSHD_PAM_SERVICE __progname 72ca86bcf2SDag-Erling Smørgrav #endif 73ca86bcf2SDag-Erling Smørgrav 74d4ecd108SDag-Erling Smørgrav /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ 75d4ecd108SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE 76076ad2f8SDag-Erling Smørgrav # define sshpam_const /* Solaris, HP-UX, SunOS */ 77d4ecd108SDag-Erling Smørgrav #else 78076ad2f8SDag-Erling Smørgrav # define sshpam_const const /* LinuxPAM, OpenPAM, AIX */ 79d4ecd108SDag-Erling Smørgrav #endif 80d4ecd108SDag-Erling Smørgrav 81333ee039SDag-Erling Smørgrav /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ 82333ee039SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE 83333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) 84333ee039SDag-Erling Smørgrav #else 85333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) 86333ee039SDag-Erling Smørgrav #endif 87333ee039SDag-Erling Smørgrav 88333ee039SDag-Erling Smørgrav #include "xmalloc.h" 89*190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 90*190cef3dSDag-Erling Smørgrav #include "ssherr.h" 91333ee039SDag-Erling Smørgrav #include "hostfile.h" 92989dd127SDag-Erling Smørgrav #include "auth.h" 93989dd127SDag-Erling Smørgrav #include "auth-pam.h" 944c5de869SBrian Feldman #include "canohost.h" 95cf2b5f3bSDag-Erling Smørgrav #include "log.h" 96cf2b5f3bSDag-Erling Smørgrav #include "msg.h" 97cf2b5f3bSDag-Erling Smørgrav #include "packet.h" 9821e764dfSDag-Erling Smørgrav #include "misc.h" 99cf2b5f3bSDag-Erling Smørgrav #include "servconf.h" 100cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h" 101cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h" 102333ee039SDag-Erling Smørgrav #ifdef GSSAPI 103333ee039SDag-Erling Smørgrav #include "ssh-gss.h" 104333ee039SDag-Erling Smørgrav #endif 105333ee039SDag-Erling Smørgrav #include "monitor_wrap.h" 106b2af61ecSKurt Lidl #include "blacklist_client.h" 10709958426SBrian Feldman 108989dd127SDag-Erling Smørgrav extern ServerOptions options; 109*190cef3dSDag-Erling Smørgrav extern struct sshbuf *loginmsg; 1105962c0e9SDag-Erling Smørgrav extern u_int utmp_len; 1112c917d39SAlfred Perlstein 112aa49c926SDag-Erling Smørgrav /* so we don't silently change behaviour */ 113cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS 114aa49c926SDag-Erling Smørgrav # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK" 115aa49c926SDag-Erling Smørgrav #endif 116aa49c926SDag-Erling Smørgrav 117aa49c926SDag-Erling Smørgrav /* 118aa49c926SDag-Erling Smørgrav * Formerly known as USE_POSIX_THREADS, using this is completely unsupported 119aa49c926SDag-Erling Smørgrav * and generally a bad idea. Use at own risk and do not expect support if 120aa49c926SDag-Erling Smørgrav * this breaks. 121aa49c926SDag-Erling Smørgrav */ 122aa49c926SDag-Erling Smørgrav #ifdef UNSUPPORTED_POSIX_THREADS_HACK 123cf2b5f3bSDag-Erling Smørgrav #include <pthread.h> 124cf2b5f3bSDag-Erling Smørgrav /* 125cf2b5f3bSDag-Erling Smørgrav * Avoid namespace clash when *not* using pthreads for systems *with* 126cf2b5f3bSDag-Erling Smørgrav * pthreads, which unconditionally define pthread_t via sys/types.h 127cf2b5f3bSDag-Erling Smørgrav * (e.g. Linux) 128cf2b5f3bSDag-Erling Smørgrav */ 129cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t; 130cf2b5f3bSDag-Erling Smørgrav #else 1311ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t; 1321ec0d754SDag-Erling Smørgrav #endif 1331ec0d754SDag-Erling Smørgrav 1341ec0d754SDag-Erling Smørgrav struct pam_ctxt { 1351ec0d754SDag-Erling Smørgrav sp_pthread_t pam_thread; 1361ec0d754SDag-Erling Smørgrav int pam_psock; 1371ec0d754SDag-Erling Smørgrav int pam_csock; 1381ec0d754SDag-Erling Smørgrav int pam_done; 1391ec0d754SDag-Erling Smørgrav }; 1401ec0d754SDag-Erling Smørgrav 1411ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *); 1421ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt; 1431ec0d754SDag-Erling Smørgrav 144aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 145cf2b5f3bSDag-Erling Smørgrav /* 146cf2b5f3bSDag-Erling Smørgrav * Simulate threads with processes. 147cf2b5f3bSDag-Erling Smørgrav */ 1481ec0d754SDag-Erling Smørgrav 1491ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1; 1501ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig; 1511ec0d754SDag-Erling Smørgrav 1521ec0d754SDag-Erling Smørgrav static void 1531ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig) 1541ec0d754SDag-Erling Smørgrav { 15521e764dfSDag-Erling Smørgrav signal(SIGCHLD, SIG_DFL); 1561ec0d754SDag-Erling Smørgrav if (cleanup_ctxt == NULL) 1571ec0d754SDag-Erling Smørgrav return; /* handler called after PAM cleanup, shouldn't happen */ 15821e764dfSDag-Erling Smørgrav if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) 15921e764dfSDag-Erling Smørgrav <= 0) { 16021e764dfSDag-Erling Smørgrav /* PAM thread has not exitted, privsep slave must have */ 16121e764dfSDag-Erling Smørgrav kill(cleanup_ctxt->pam_thread, SIGTERM); 162076ad2f8SDag-Erling Smørgrav while (waitpid(cleanup_ctxt->pam_thread, 163076ad2f8SDag-Erling Smørgrav &sshpam_thread_status, 0) == -1) { 164076ad2f8SDag-Erling Smørgrav if (errno == EINTR) 165076ad2f8SDag-Erling Smørgrav continue; 166076ad2f8SDag-Erling Smørgrav return; 167076ad2f8SDag-Erling Smørgrav } 16821e764dfSDag-Erling Smørgrav } 1691ec0d754SDag-Erling Smørgrav if (WIFSIGNALED(sshpam_thread_status) && 1701ec0d754SDag-Erling Smørgrav WTERMSIG(sshpam_thread_status) == SIGTERM) 1711ec0d754SDag-Erling Smørgrav return; /* terminated by pthread_cancel */ 1721ec0d754SDag-Erling Smørgrav if (!WIFEXITED(sshpam_thread_status)) 173d4af9e69SDag-Erling Smørgrav sigdie("PAM: authentication thread exited unexpectedly"); 1741ec0d754SDag-Erling Smørgrav if (WEXITSTATUS(sshpam_thread_status) != 0) 175d4af9e69SDag-Erling Smørgrav sigdie("PAM: authentication thread exited uncleanly"); 1761ec0d754SDag-Erling Smørgrav } 17709958426SBrian Feldman 178333ee039SDag-Erling Smørgrav /* ARGSUSED */ 179cf2b5f3bSDag-Erling Smørgrav static void 180333ee039SDag-Erling Smørgrav pthread_exit(void *value) 18109958426SBrian Feldman { 182cf2b5f3bSDag-Erling Smørgrav _exit(0); 18309958426SBrian Feldman } 18409958426SBrian Feldman 185333ee039SDag-Erling Smørgrav /* ARGSUSED */ 186cf2b5f3bSDag-Erling Smørgrav static int 187333ee039SDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr, 188cf2b5f3bSDag-Erling Smørgrav void *(*thread_start)(void *), void *arg) 189cf2b5f3bSDag-Erling Smørgrav { 190cf2b5f3bSDag-Erling Smørgrav pid_t pid; 191d4ecd108SDag-Erling Smørgrav struct pam_ctxt *ctx = arg; 192cf2b5f3bSDag-Erling Smørgrav 1935962c0e9SDag-Erling Smørgrav sshpam_thread_status = -1; 194cf2b5f3bSDag-Erling Smørgrav switch ((pid = fork())) { 195cf2b5f3bSDag-Erling Smørgrav case -1: 196cf2b5f3bSDag-Erling Smørgrav error("fork(): %s", strerror(errno)); 197cf2b5f3bSDag-Erling Smørgrav return (-1); 198cf2b5f3bSDag-Erling Smørgrav case 0: 199d4ecd108SDag-Erling Smørgrav close(ctx->pam_psock); 200d4ecd108SDag-Erling Smørgrav ctx->pam_psock = -1; 201cf2b5f3bSDag-Erling Smørgrav thread_start(arg); 202cf2b5f3bSDag-Erling Smørgrav _exit(1); 203cf2b5f3bSDag-Erling Smørgrav default: 204cf2b5f3bSDag-Erling Smørgrav *thread = pid; 205d4ecd108SDag-Erling Smørgrav close(ctx->pam_csock); 206d4ecd108SDag-Erling Smørgrav ctx->pam_csock = -1; 2071ec0d754SDag-Erling Smørgrav sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); 208cf2b5f3bSDag-Erling Smørgrav return (0); 209cf2b5f3bSDag-Erling Smørgrav } 210cf2b5f3bSDag-Erling Smørgrav } 211cf2b5f3bSDag-Erling Smørgrav 212cf2b5f3bSDag-Erling Smørgrav static int 213cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread) 214cf2b5f3bSDag-Erling Smørgrav { 2151ec0d754SDag-Erling Smørgrav signal(SIGCHLD, sshpam_oldsig); 216cf2b5f3bSDag-Erling Smørgrav return (kill(thread, SIGTERM)); 217cf2b5f3bSDag-Erling Smørgrav } 218cf2b5f3bSDag-Erling Smørgrav 219333ee039SDag-Erling Smørgrav /* ARGSUSED */ 220cf2b5f3bSDag-Erling Smørgrav static int 221333ee039SDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value) 222cf2b5f3bSDag-Erling Smørgrav { 223cf2b5f3bSDag-Erling Smørgrav int status; 224cf2b5f3bSDag-Erling Smørgrav 2251ec0d754SDag-Erling Smørgrav if (sshpam_thread_status != -1) 2261ec0d754SDag-Erling Smørgrav return (sshpam_thread_status); 2271ec0d754SDag-Erling Smørgrav signal(SIGCHLD, sshpam_oldsig); 228076ad2f8SDag-Erling Smørgrav while (waitpid(thread, &status, 0) == -1) { 229076ad2f8SDag-Erling Smørgrav if (errno == EINTR) 230076ad2f8SDag-Erling Smørgrav continue; 231076ad2f8SDag-Erling Smørgrav fatal("%s: waitpid: %s", __func__, strerror(errno)); 232076ad2f8SDag-Erling Smørgrav } 233cf2b5f3bSDag-Erling Smørgrav return (status); 234cf2b5f3bSDag-Erling Smørgrav } 235cf2b5f3bSDag-Erling Smørgrav #endif 236cf2b5f3bSDag-Erling Smørgrav 237cf2b5f3bSDag-Erling Smørgrav 238cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL; 239cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0; 240cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0; 241cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0; 242cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0; 2431ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1; 244076ad2f8SDag-Erling Smørgrav static int sshpam_maxtries_reached = 0; 2451ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL; 2465962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL; 24721e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL; 248cf2b5f3bSDag-Erling Smørgrav 2491ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */ 2501ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST 2511ec0d754SDag-Erling Smørgrav static char ** 2521ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh) 2531ec0d754SDag-Erling Smørgrav { 2541ec0d754SDag-Erling Smørgrav /* 2551ec0d754SDag-Erling Smørgrav * XXX - If necessary, we can still support envrionment passing 2561ec0d754SDag-Erling Smørgrav * for platforms without pam_getenvlist by searching for known 2571ec0d754SDag-Erling Smørgrav * env vars (e.g. KRB5CCNAME) from the PAM environment. 2581ec0d754SDag-Erling Smørgrav */ 2591ec0d754SDag-Erling Smørgrav return NULL; 2601ec0d754SDag-Erling Smørgrav } 2611ec0d754SDag-Erling Smørgrav #endif 262cf2b5f3bSDag-Erling Smørgrav 26321e764dfSDag-Erling Smørgrav /* 26421e764dfSDag-Erling Smørgrav * Some platforms, notably Solaris, do not enforce password complexity 26521e764dfSDag-Erling Smørgrav * rules during pam_chauthtok() if the real uid of the calling process 26621e764dfSDag-Erling Smørgrav * is 0, on the assumption that it's being called by "passwd" run by root. 26721e764dfSDag-Erling Smørgrav * This wraps pam_chauthtok and sets/restore the real uid so PAM will do 26821e764dfSDag-Erling Smørgrav * the right thing. 26921e764dfSDag-Erling Smørgrav */ 27021e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID 27121e764dfSDag-Erling Smørgrav static int 27221e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) 27321e764dfSDag-Erling Smørgrav { 27421e764dfSDag-Erling Smørgrav int result; 27521e764dfSDag-Erling Smørgrav 27621e764dfSDag-Erling Smørgrav if (sshpam_authctxt == NULL) 27721e764dfSDag-Erling Smørgrav fatal("PAM: sshpam_authctxt not initialized"); 27821e764dfSDag-Erling Smørgrav if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) 27921e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 28021e764dfSDag-Erling Smørgrav result = pam_chauthtok(pamh, flags); 28121e764dfSDag-Erling Smørgrav if (setreuid(0, -1) == -1) 28221e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 28321e764dfSDag-Erling Smørgrav return result; 28421e764dfSDag-Erling Smørgrav } 28521e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) 28621e764dfSDag-Erling Smørgrav #endif 28721e764dfSDag-Erling Smørgrav 2881ec0d754SDag-Erling Smørgrav void 28921e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd) 2901ec0d754SDag-Erling Smørgrav { 29147dd1d1bSDag-Erling Smørgrav extern struct sshauthopt *auth_opts; 29247dd1d1bSDag-Erling Smørgrav static int saved_port, saved_agent, saved_x11; 29347dd1d1bSDag-Erling Smørgrav 2941ec0d754SDag-Erling Smørgrav debug3("%s %d", __func__, reqd); 2955962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 2965962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 2975962c0e9SDag-Erling Smørgrav sshpam_authctxt->force_pwchange = reqd; 2981ec0d754SDag-Erling Smørgrav if (reqd) { 29947dd1d1bSDag-Erling Smørgrav saved_port = auth_opts->permit_port_forwarding_flag; 30047dd1d1bSDag-Erling Smørgrav saved_agent = auth_opts->permit_agent_forwarding_flag; 30147dd1d1bSDag-Erling Smørgrav saved_x11 = auth_opts->permit_x11_forwarding_flag; 30247dd1d1bSDag-Erling Smørgrav auth_opts->permit_port_forwarding_flag = 0; 30347dd1d1bSDag-Erling Smørgrav auth_opts->permit_agent_forwarding_flag = 0; 30447dd1d1bSDag-Erling Smørgrav auth_opts->permit_x11_forwarding_flag = 0; 3051ec0d754SDag-Erling Smørgrav } else { 30647dd1d1bSDag-Erling Smørgrav if (saved_port) 30747dd1d1bSDag-Erling Smørgrav auth_opts->permit_port_forwarding_flag = saved_port; 30847dd1d1bSDag-Erling Smørgrav if (saved_agent) 30947dd1d1bSDag-Erling Smørgrav auth_opts->permit_agent_forwarding_flag = saved_agent; 31047dd1d1bSDag-Erling Smørgrav if (saved_x11) 31147dd1d1bSDag-Erling Smørgrav auth_opts->permit_x11_forwarding_flag = saved_x11; 3121ec0d754SDag-Erling Smørgrav } 3131ec0d754SDag-Erling Smørgrav } 3141ec0d754SDag-Erling Smørgrav 3151ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */ 3161ec0d754SDag-Erling Smørgrav static void 317*190cef3dSDag-Erling Smørgrav import_environments(struct sshbuf *b) 3181ec0d754SDag-Erling Smørgrav { 3191ec0d754SDag-Erling Smørgrav char *env; 320*190cef3dSDag-Erling Smørgrav u_int n, i, num_env; 321*190cef3dSDag-Erling Smørgrav int r; 3221ec0d754SDag-Erling Smørgrav 3231ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 3241ec0d754SDag-Erling Smørgrav 325aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 3261ec0d754SDag-Erling Smørgrav /* Import variables set by do_pam_account */ 327*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &n)) != 0) 328*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 329*190cef3dSDag-Erling Smørgrav if (n > INT_MAX) 330*190cef3dSDag-Erling Smørgrav fatal("%s: invalid PAM account status %u", __func__, n); 331*190cef3dSDag-Erling Smørgrav sshpam_account_status = (int)n; 332*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &n)) != 0) 333*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 334*190cef3dSDag-Erling Smørgrav sshpam_password_change_required(n != 0); 3351ec0d754SDag-Erling Smørgrav 3361ec0d754SDag-Erling Smørgrav /* Import environment from subprocess */ 337*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &num_env)) != 0) 338*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 339333ee039SDag-Erling Smørgrav if (num_env > 1024) 340333ee039SDag-Erling Smørgrav fatal("%s: received %u environment variables, expected <= 1024", 341333ee039SDag-Erling Smørgrav __func__, num_env); 342333ee039SDag-Erling Smørgrav sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); 3431ec0d754SDag-Erling Smørgrav debug3("PAM: num env strings %d", num_env); 344*190cef3dSDag-Erling Smørgrav for(i = 0; i < num_env; i++) { 345*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(b, &(sshpam_env[i]), NULL)) != 0) 346*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 347*190cef3dSDag-Erling Smørgrav } 3481ec0d754SDag-Erling Smørgrav sshpam_env[num_env] = NULL; 3491ec0d754SDag-Erling Smørgrav 3501ec0d754SDag-Erling Smørgrav /* Import PAM environment from subprocess */ 351*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u32(b, &num_env)) != 0) 352*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 3531ec0d754SDag-Erling Smørgrav debug("PAM: num PAM env strings %d", num_env); 3541ec0d754SDag-Erling Smørgrav for (i = 0; i < num_env; i++) { 355*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(b, &env, NULL)) != 0) 356*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 3571ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV 3581ec0d754SDag-Erling Smørgrav /* Errors are not fatal here */ 359*190cef3dSDag-Erling Smørgrav if ((r = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { 3601ec0d754SDag-Erling Smørgrav error("PAM: pam_putenv: %s", 361*190cef3dSDag-Erling Smørgrav pam_strerror(sshpam_handle, r)); 3621ec0d754SDag-Erling Smørgrav } 3631ec0d754SDag-Erling Smørgrav #endif 364*190cef3dSDag-Erling Smørgrav /* XXX leak env? */ 3651ec0d754SDag-Erling Smørgrav } 3665962c0e9SDag-Erling Smørgrav #endif 3671ec0d754SDag-Erling Smørgrav } 368cf2b5f3bSDag-Erling Smørgrav 369cf2b5f3bSDag-Erling Smørgrav /* 370cf2b5f3bSDag-Erling Smørgrav * Conversation function for authentication thread. 371cf2b5f3bSDag-Erling Smørgrav */ 372cf2b5f3bSDag-Erling Smørgrav static int 373d4ecd108SDag-Erling Smørgrav sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, 374cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 375cf2b5f3bSDag-Erling Smørgrav { 376*190cef3dSDag-Erling Smørgrav struct sshbuf *buffer; 377cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 378cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 379*190cef3dSDag-Erling Smørgrav int r, i; 380*190cef3dSDag-Erling Smørgrav u_char status; 381cf2b5f3bSDag-Erling Smørgrav 3821ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 383cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 384cf2b5f3bSDag-Erling Smørgrav 38521e764dfSDag-Erling Smørgrav if (data == NULL) { 38621e764dfSDag-Erling Smørgrav error("PAM: conversation function passed a null context"); 38721e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 38821e764dfSDag-Erling Smørgrav } 389cf2b5f3bSDag-Erling Smørgrav ctxt = data; 390cf2b5f3bSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 391cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 392cf2b5f3bSDag-Erling Smørgrav 393333ee039SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 394*190cef3dSDag-Erling Smørgrav return PAM_CONV_ERR; 395*190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) { 396*190cef3dSDag-Erling Smørgrav free(reply); 397*190cef3dSDag-Erling Smørgrav return PAM_CONV_ERR; 398*190cef3dSDag-Erling Smørgrav } 399cf2b5f3bSDag-Erling Smørgrav 400cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 401cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 402cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 403cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 404*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, 405*190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 406*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 407*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 4081ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 409*190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) 4101ec0d754SDag-Erling Smørgrav goto fail; 411*190cef3dSDag-Erling Smørgrav 412*190cef3dSDag-Erling Smørgrav if (ssh_msg_recv(ctxt->pam_csock, buffer) == -1) 4131ec0d754SDag-Erling Smørgrav goto fail; 414*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u8(buffer, &status)) != 0) 415*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 416*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 417*190cef3dSDag-Erling Smørgrav if (status != PAM_AUTHTOK) 418cf2b5f3bSDag-Erling Smørgrav goto fail; 419*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(buffer, 420*190cef3dSDag-Erling Smørgrav &reply[i].resp, NULL)) != 0) 421*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 422*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 423cf2b5f3bSDag-Erling Smørgrav break; 424cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 425cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 426*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, 427*190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 428*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 429*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 4301ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 431*190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) 4321ec0d754SDag-Erling Smørgrav goto fail; 433cf2b5f3bSDag-Erling Smørgrav break; 434cf2b5f3bSDag-Erling Smørgrav default: 435cf2b5f3bSDag-Erling Smørgrav goto fail; 436cf2b5f3bSDag-Erling Smørgrav } 437*190cef3dSDag-Erling Smørgrav sshbuf_reset(buffer); 438cf2b5f3bSDag-Erling Smørgrav } 439*190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 440cf2b5f3bSDag-Erling Smørgrav *resp = reply; 441cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 442cf2b5f3bSDag-Erling Smørgrav 443cf2b5f3bSDag-Erling Smørgrav fail: 444cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 445e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 446cf2b5f3bSDag-Erling Smørgrav } 447e4a9863fSDag-Erling Smørgrav free(reply); 448*190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 449cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 450cf2b5f3bSDag-Erling Smørgrav } 451cf2b5f3bSDag-Erling Smørgrav 452cf2b5f3bSDag-Erling Smørgrav /* 453cf2b5f3bSDag-Erling Smørgrav * Authentication thread. 454cf2b5f3bSDag-Erling Smørgrav */ 455cf2b5f3bSDag-Erling Smørgrav static void * 456cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp) 457cf2b5f3bSDag-Erling Smørgrav { 458cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 459*190cef3dSDag-Erling Smørgrav struct sshbuf *buffer = NULL; 460cf2b5f3bSDag-Erling Smørgrav struct pam_conv sshpam_conv; 461*190cef3dSDag-Erling Smørgrav int r, flags = (options.permit_empty_passwd == 0 ? 46221e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 463aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 4641ec0d754SDag-Erling Smørgrav extern char **environ; 4651ec0d754SDag-Erling Smørgrav char **env_from_pam; 4661ec0d754SDag-Erling Smørgrav u_int i; 467cf2b5f3bSDag-Erling Smørgrav const char *pam_user; 468d4ecd108SDag-Erling Smørgrav const char **ptr_pam_user = &pam_user; 469333ee039SDag-Erling Smørgrav char *tz = getenv("TZ"); 470cf2b5f3bSDag-Erling Smørgrav 471f7167e0eSDag-Erling Smørgrav sshpam_err = pam_get_item(sshpam_handle, PAM_USER, 472d4ecd108SDag-Erling Smørgrav (sshpam_const void **)ptr_pam_user); 473f7167e0eSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 474f7167e0eSDag-Erling Smørgrav goto auth_fail; 475333ee039SDag-Erling Smørgrav 4761ec0d754SDag-Erling Smørgrav environ[0] = NULL; 477333ee039SDag-Erling Smørgrav if (tz != NULL) 478333ee039SDag-Erling Smørgrav if (setenv("TZ", tz, 1) == -1) 479333ee039SDag-Erling Smørgrav error("PAM: could not set TZ environment: %s", 480333ee039SDag-Erling Smørgrav strerror(errno)); 48121e764dfSDag-Erling Smørgrav 48221e764dfSDag-Erling Smørgrav if (sshpam_authctxt != NULL) { 48321e764dfSDag-Erling Smørgrav setproctitle("%s [pam]", 48421e764dfSDag-Erling Smørgrav sshpam_authctxt->valid ? pam_user : "unknown"); 48521e764dfSDag-Erling Smørgrav } 486cf2b5f3bSDag-Erling Smørgrav #endif 487cf2b5f3bSDag-Erling Smørgrav 488cf2b5f3bSDag-Erling Smørgrav sshpam_conv.conv = sshpam_thread_conv; 489cf2b5f3bSDag-Erling Smørgrav sshpam_conv.appdata_ptr = ctxt; 490cf2b5f3bSDag-Erling Smørgrav 4915962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 4925962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 4935962c0e9SDag-Erling Smørgrav 494*190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) 495*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 496*190cef3dSDag-Erling Smørgrav 497cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 498cf2b5f3bSDag-Erling Smørgrav (const void *)&sshpam_conv); 499cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 500cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 50121e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 502076ad2f8SDag-Erling Smørgrav if (sshpam_err == PAM_MAXTRIES) 503076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(1); 504cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 505cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 5061ec0d754SDag-Erling Smørgrav 507333ee039SDag-Erling Smørgrav if (!do_pam_account()) { 508333ee039SDag-Erling Smørgrav sshpam_err = PAM_ACCT_EXPIRED; 5091ec0d754SDag-Erling Smørgrav goto auth_fail; 510333ee039SDag-Erling Smørgrav } 5115962c0e9SDag-Erling Smørgrav if (sshpam_authctxt->force_pwchange) { 5121ec0d754SDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, 5131ec0d754SDag-Erling Smørgrav PAM_CHANGE_EXPIRED_AUTHTOK); 5141ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 5151ec0d754SDag-Erling Smørgrav goto auth_fail; 51621e764dfSDag-Erling Smørgrav sshpam_password_change_required(0); 5171ec0d754SDag-Erling Smørgrav } 5181ec0d754SDag-Erling Smørgrav 519*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, "OK")) != 0) 520*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5211ec0d754SDag-Erling Smørgrav 522aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 5231ec0d754SDag-Erling Smørgrav /* Export variables set by do_pam_account */ 524*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 || 525*190cef3dSDag-Erling Smørgrav (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0) 526*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5271ec0d754SDag-Erling Smørgrav 5281ec0d754SDag-Erling Smørgrav /* Export any environment strings set in child */ 529*190cef3dSDag-Erling Smørgrav for (i = 0; environ[i] != NULL; i++) { 530*190cef3dSDag-Erling Smørgrav /* Count */ 531*190cef3dSDag-Erling Smørgrav if (i > INT_MAX) 532*190cef3dSDag-Erling Smørgrav fatal("%s: too many enviornment strings", __func__); 533*190cef3dSDag-Erling Smørgrav } 534*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(buffer, i)) != 0) 535*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 536*190cef3dSDag-Erling Smørgrav for (i = 0; environ[i] != NULL; i++) { 537*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, environ[i])) != 0) 538*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 539*190cef3dSDag-Erling Smørgrav } 5401ec0d754SDag-Erling Smørgrav /* Export any environment strings set by PAM in child */ 5411ec0d754SDag-Erling Smørgrav env_from_pam = pam_getenvlist(sshpam_handle); 542*190cef3dSDag-Erling Smørgrav for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { 543*190cef3dSDag-Erling Smørgrav /* Count */ 544*190cef3dSDag-Erling Smørgrav if (i > INT_MAX) 545*190cef3dSDag-Erling Smørgrav fatal("%s: too many PAM enviornment strings", __func__); 546*190cef3dSDag-Erling Smørgrav } 547*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u32(buffer, i)) != 0) 548*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 549*190cef3dSDag-Erling Smørgrav for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { 550*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, env_from_pam[i])) != 0) 551*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 552*190cef3dSDag-Erling Smørgrav } 553aa49c926SDag-Erling Smørgrav #endif /* UNSUPPORTED_POSIX_THREADS_HACK */ 5541ec0d754SDag-Erling Smørgrav 5551ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 556*190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer); 557*190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 558cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 559cf2b5f3bSDag-Erling Smørgrav 560cf2b5f3bSDag-Erling Smørgrav auth_fail: 561*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, 562*190cef3dSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err))) != 0) 563*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5641ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 565333ee039SDag-Erling Smørgrav if (sshpam_err == PAM_ACCT_EXPIRED) 566*190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer); 567076ad2f8SDag-Erling Smørgrav else if (sshpam_maxtries_reached) 568*190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer); 569333ee039SDag-Erling Smørgrav else 570*190cef3dSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, buffer); 571*190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 572cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 573cf2b5f3bSDag-Erling Smørgrav 574cf2b5f3bSDag-Erling Smørgrav return (NULL); /* Avoid warning for non-pthread case */ 575cf2b5f3bSDag-Erling Smørgrav } 576cf2b5f3bSDag-Erling Smørgrav 5771ec0d754SDag-Erling Smørgrav void 5781ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void) 579cf2b5f3bSDag-Erling Smørgrav { 5801ec0d754SDag-Erling Smørgrav struct pam_ctxt *ctxt = cleanup_ctxt; 581cf2b5f3bSDag-Erling Smørgrav 5821ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 5831ec0d754SDag-Erling Smørgrav if (ctxt != NULL && ctxt->pam_thread != 0) { 584cf2b5f3bSDag-Erling Smørgrav pthread_cancel(ctxt->pam_thread); 585cf2b5f3bSDag-Erling Smørgrav pthread_join(ctxt->pam_thread, NULL); 586cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_psock); 587cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_csock); 5881ec0d754SDag-Erling Smørgrav memset(ctxt, 0, sizeof(*ctxt)); 5891ec0d754SDag-Erling Smørgrav cleanup_ctxt = NULL; 5901ec0d754SDag-Erling Smørgrav } 591cf2b5f3bSDag-Erling Smørgrav } 592cf2b5f3bSDag-Erling Smørgrav 593cf2b5f3bSDag-Erling Smørgrav static int 594d4ecd108SDag-Erling Smørgrav sshpam_null_conv(int n, sshpam_const struct pam_message **msg, 595cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 596cf2b5f3bSDag-Erling Smørgrav { 5971ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 598cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 599cf2b5f3bSDag-Erling Smørgrav } 600cf2b5f3bSDag-Erling Smørgrav 601cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 602cf2b5f3bSDag-Erling Smørgrav 603aa49c926SDag-Erling Smørgrav static int 604d4ecd108SDag-Erling Smørgrav sshpam_store_conv(int n, sshpam_const struct pam_message **msg, 605aa49c926SDag-Erling Smørgrav struct pam_response **resp, void *data) 606aa49c926SDag-Erling Smørgrav { 607aa49c926SDag-Erling Smørgrav struct pam_response *reply; 608*190cef3dSDag-Erling Smørgrav int r, i; 609aa49c926SDag-Erling Smørgrav 610aa49c926SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 611aa49c926SDag-Erling Smørgrav *resp = NULL; 612aa49c926SDag-Erling Smørgrav 613aa49c926SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 614aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 615aa49c926SDag-Erling Smørgrav 616333ee039SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 617aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 618aa49c926SDag-Erling Smørgrav 619aa49c926SDag-Erling Smørgrav for (i = 0; i < n; ++i) { 620aa49c926SDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 621aa49c926SDag-Erling Smørgrav case PAM_ERROR_MSG: 622aa49c926SDag-Erling Smørgrav case PAM_TEXT_INFO: 623*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(loginmsg, "%s\n", 624*190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 625*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 626*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 627aa49c926SDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 628aa49c926SDag-Erling Smørgrav break; 629aa49c926SDag-Erling Smørgrav default: 630aa49c926SDag-Erling Smørgrav goto fail; 631aa49c926SDag-Erling Smørgrav } 632aa49c926SDag-Erling Smørgrav } 633aa49c926SDag-Erling Smørgrav *resp = reply; 634aa49c926SDag-Erling Smørgrav return (PAM_SUCCESS); 635aa49c926SDag-Erling Smørgrav 636aa49c926SDag-Erling Smørgrav fail: 637aa49c926SDag-Erling Smørgrav for(i = 0; i < n; i++) { 638e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 639aa49c926SDag-Erling Smørgrav } 640e4a9863fSDag-Erling Smørgrav free(reply); 641aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 642aa49c926SDag-Erling Smørgrav } 643aa49c926SDag-Erling Smørgrav 644aa49c926SDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL }; 645aa49c926SDag-Erling Smørgrav 6461ec0d754SDag-Erling Smørgrav void 6471ec0d754SDag-Erling Smørgrav sshpam_cleanup(void) 648cf2b5f3bSDag-Erling Smørgrav { 649d4af9e69SDag-Erling Smørgrav if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor())) 650cf2b5f3bSDag-Erling Smørgrav return; 651d4af9e69SDag-Erling Smørgrav debug("PAM: cleanup"); 652cf2b5f3bSDag-Erling Smørgrav pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); 653cf2b5f3bSDag-Erling Smørgrav if (sshpam_session_open) { 654d4af9e69SDag-Erling Smørgrav debug("PAM: closing session"); 655cf2b5f3bSDag-Erling Smørgrav pam_close_session(sshpam_handle, PAM_SILENT); 656cf2b5f3bSDag-Erling Smørgrav sshpam_session_open = 0; 657cf2b5f3bSDag-Erling Smørgrav } 6587aee6ffeSDag-Erling Smørgrav if (sshpam_cred_established) { 6597aee6ffeSDag-Erling Smørgrav debug("PAM: deleting credentials"); 6607aee6ffeSDag-Erling Smørgrav pam_setcred(sshpam_handle, PAM_DELETE_CRED); 6617aee6ffeSDag-Erling Smørgrav sshpam_cred_established = 0; 6627aee6ffeSDag-Erling Smørgrav } 6631ec0d754SDag-Erling Smørgrav sshpam_authenticated = 0; 664cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 665cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 666cf2b5f3bSDag-Erling Smørgrav } 667cf2b5f3bSDag-Erling Smørgrav 668cf2b5f3bSDag-Erling Smørgrav static int 6695962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt) 670cf2b5f3bSDag-Erling Smørgrav { 6715962c0e9SDag-Erling Smørgrav const char *pam_rhost, *pam_user, *user = authctxt->user; 672d4ecd108SDag-Erling Smørgrav const char **ptr_pam_user = &pam_user; 673076ad2f8SDag-Erling Smørgrav struct ssh *ssh = active_state; /* XXX */ 674cf2b5f3bSDag-Erling Smørgrav 675cf2b5f3bSDag-Erling Smørgrav if (sshpam_handle != NULL) { 676cf2b5f3bSDag-Erling Smørgrav /* We already have a PAM context; check if the user matches */ 677cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_get_item(sshpam_handle, 678d4ecd108SDag-Erling Smørgrav PAM_USER, (sshpam_const void **)ptr_pam_user); 679cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 680cf2b5f3bSDag-Erling Smørgrav return (0); 681cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 682cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 683cf2b5f3bSDag-Erling Smørgrav } 684cf2b5f3bSDag-Erling Smørgrav debug("PAM: initializing for \"%s\"", user); 685cf2b5f3bSDag-Erling Smørgrav sshpam_err = 686aa49c926SDag-Erling Smørgrav pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); 6875962c0e9SDag-Erling Smørgrav sshpam_authctxt = authctxt; 6885962c0e9SDag-Erling Smørgrav 689cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 690cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 691cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 692cf2b5f3bSDag-Erling Smørgrav return (-1); 693cf2b5f3bSDag-Erling Smørgrav } 694076ad2f8SDag-Erling Smørgrav pam_rhost = auth_get_canonical_hostname(ssh, options.use_dns); 695cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); 696cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); 697cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 698cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 699cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 700cf2b5f3bSDag-Erling Smørgrav return (-1); 701cf2b5f3bSDag-Erling Smørgrav } 702cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE 703cf2b5f3bSDag-Erling Smørgrav /* 704cf2b5f3bSDag-Erling Smørgrav * Some silly PAM modules (e.g. pam_time) require a TTY to operate. 705cf2b5f3bSDag-Erling Smørgrav * sshd doesn't set the tty until too late in the auth process and 706cf2b5f3bSDag-Erling Smørgrav * may not even set one (for tty-less connections) 707cf2b5f3bSDag-Erling Smørgrav */ 708cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_TTY to \"ssh\""); 709cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); 710cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 711cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 712cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 713cf2b5f3bSDag-Erling Smørgrav return (-1); 714cf2b5f3bSDag-Erling Smørgrav } 715cf2b5f3bSDag-Erling Smørgrav #endif 716cf2b5f3bSDag-Erling Smørgrav return (0); 717cf2b5f3bSDag-Erling Smørgrav } 718cf2b5f3bSDag-Erling Smørgrav 719*190cef3dSDag-Erling Smørgrav static void 720*190cef3dSDag-Erling Smørgrav expose_authinfo(const char *caller) 721*190cef3dSDag-Erling Smørgrav { 722*190cef3dSDag-Erling Smørgrav char *auth_info; 723*190cef3dSDag-Erling Smørgrav 724*190cef3dSDag-Erling Smørgrav /* 725*190cef3dSDag-Erling Smørgrav * Expose authentication information to PAM. 726*190cef3dSDag-Erling Smørgrav * The environment variable is versioned. Please increment the 727*190cef3dSDag-Erling Smørgrav * version suffix if the format of session_info changes. 728*190cef3dSDag-Erling Smørgrav */ 729*190cef3dSDag-Erling Smørgrav if (sshpam_authctxt->session_info == NULL) 730*190cef3dSDag-Erling Smørgrav auth_info = xstrdup(""); 731*190cef3dSDag-Erling Smørgrav else if ((auth_info = sshbuf_dup_string( 732*190cef3dSDag-Erling Smørgrav sshpam_authctxt->session_info)) == NULL) 733*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_dup_string failed", __func__); 734*190cef3dSDag-Erling Smørgrav 735*190cef3dSDag-Erling Smørgrav debug2("%s: auth information in SSH_AUTH_INFO_0", caller); 736*190cef3dSDag-Erling Smørgrav do_pam_putenv("SSH_AUTH_INFO_0", auth_info); 737*190cef3dSDag-Erling Smørgrav free(auth_info); 738*190cef3dSDag-Erling Smørgrav } 739*190cef3dSDag-Erling Smørgrav 740cf2b5f3bSDag-Erling Smørgrav static void * 741cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt) 742cf2b5f3bSDag-Erling Smørgrav { 743cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 744cf2b5f3bSDag-Erling Smørgrav int socks[2]; 745cf2b5f3bSDag-Erling Smørgrav 7461ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 747333ee039SDag-Erling Smørgrav /* 748333ee039SDag-Erling Smørgrav * Refuse to start if we don't have PAM enabled or do_pam_account 749333ee039SDag-Erling Smørgrav * has previously failed. 750333ee039SDag-Erling Smørgrav */ 751333ee039SDag-Erling Smørgrav if (!options.use_pam || sshpam_account_status == 0) 752cf2b5f3bSDag-Erling Smørgrav return NULL; 753cf2b5f3bSDag-Erling Smørgrav 754cf2b5f3bSDag-Erling Smørgrav /* Initialize PAM */ 7555962c0e9SDag-Erling Smørgrav if (sshpam_init(authctxt) == -1) { 756cf2b5f3bSDag-Erling Smørgrav error("PAM: initialization failed"); 757cf2b5f3bSDag-Erling Smørgrav return (NULL); 758cf2b5f3bSDag-Erling Smørgrav } 759cf2b5f3bSDag-Erling Smørgrav 760*190cef3dSDag-Erling Smørgrav expose_authinfo(__func__); 761d4af9e69SDag-Erling Smørgrav ctxt = xcalloc(1, sizeof *ctxt); 7621ec0d754SDag-Erling Smørgrav 763cf2b5f3bSDag-Erling Smørgrav /* Start the authentication thread */ 764cf2b5f3bSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 765cf2b5f3bSDag-Erling Smørgrav error("PAM: failed create sockets: %s", strerror(errno)); 766e4a9863fSDag-Erling Smørgrav free(ctxt); 767cf2b5f3bSDag-Erling Smørgrav return (NULL); 768cf2b5f3bSDag-Erling Smørgrav } 769cf2b5f3bSDag-Erling Smørgrav ctxt->pam_psock = socks[0]; 770cf2b5f3bSDag-Erling Smørgrav ctxt->pam_csock = socks[1]; 771cf2b5f3bSDag-Erling Smørgrav if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { 772cf2b5f3bSDag-Erling Smørgrav error("PAM: failed to start authentication thread: %s", 773cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 774cf2b5f3bSDag-Erling Smørgrav close(socks[0]); 775cf2b5f3bSDag-Erling Smørgrav close(socks[1]); 776e4a9863fSDag-Erling Smørgrav free(ctxt); 777cf2b5f3bSDag-Erling Smørgrav return (NULL); 778cf2b5f3bSDag-Erling Smørgrav } 7791ec0d754SDag-Erling Smørgrav cleanup_ctxt = ctxt; 780cf2b5f3bSDag-Erling Smørgrav return (ctxt); 781cf2b5f3bSDag-Erling Smørgrav } 782cf2b5f3bSDag-Erling Smørgrav 783cf2b5f3bSDag-Erling Smørgrav static int 784cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info, 785cf2b5f3bSDag-Erling Smørgrav u_int *num, char ***prompts, u_int **echo_on) 786cf2b5f3bSDag-Erling Smørgrav { 787076ad2f8SDag-Erling Smørgrav struct ssh *ssh = active_state; /* XXX */ 788*190cef3dSDag-Erling Smørgrav struct sshbuf *buffer; 789cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 790cf2b5f3bSDag-Erling Smørgrav size_t plen; 791cf2b5f3bSDag-Erling Smørgrav u_char type; 792cf2b5f3bSDag-Erling Smørgrav char *msg; 793aa49c926SDag-Erling Smørgrav size_t len, mlen; 794*190cef3dSDag-Erling Smørgrav int r; 795cf2b5f3bSDag-Erling Smørgrav 7961ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 797*190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) 798*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 799cf2b5f3bSDag-Erling Smørgrav *name = xstrdup(""); 800cf2b5f3bSDag-Erling Smørgrav *info = xstrdup(""); 801cf2b5f3bSDag-Erling Smørgrav *prompts = xmalloc(sizeof(char *)); 802cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 803cf2b5f3bSDag-Erling Smørgrav plen = 0; 804cf2b5f3bSDag-Erling Smørgrav *echo_on = xmalloc(sizeof(u_int)); 805*190cef3dSDag-Erling Smørgrav while (ssh_msg_recv(ctxt->pam_psock, buffer) == 0) { 806*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_get_u8(buffer, &type)) != 0 || 807*190cef3dSDag-Erling Smørgrav (r = sshbuf_get_cstring(buffer, &msg, &mlen)) != 0) 808*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 809cf2b5f3bSDag-Erling Smørgrav switch (type) { 810cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 811cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 812cf2b5f3bSDag-Erling Smørgrav *num = 1; 813aa49c926SDag-Erling Smørgrav len = plen + mlen + 1; 814557f75e5SDag-Erling Smørgrav **prompts = xreallocarray(**prompts, 1, len); 815aa49c926SDag-Erling Smørgrav strlcpy(**prompts + plen, msg, len - plen); 816aa49c926SDag-Erling Smørgrav plen += mlen; 817cf2b5f3bSDag-Erling Smørgrav **echo_on = (type == PAM_PROMPT_ECHO_ON); 818e4a9863fSDag-Erling Smørgrav free(msg); 819cf2b5f3bSDag-Erling Smørgrav return (0); 820cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 821cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 822cf2b5f3bSDag-Erling Smørgrav /* accumulate messages */ 823aa49c926SDag-Erling Smørgrav len = plen + mlen + 2; 824557f75e5SDag-Erling Smørgrav **prompts = xreallocarray(**prompts, 1, len); 825aa49c926SDag-Erling Smørgrav strlcpy(**prompts + plen, msg, len - plen); 826aa49c926SDag-Erling Smørgrav plen += mlen; 827aa49c926SDag-Erling Smørgrav strlcat(**prompts + plen, "\n", len - plen); 828aa49c926SDag-Erling Smørgrav plen++; 829e4a9863fSDag-Erling Smørgrav free(msg); 830cf2b5f3bSDag-Erling Smørgrav break; 831333ee039SDag-Erling Smørgrav case PAM_ACCT_EXPIRED: 832076ad2f8SDag-Erling Smørgrav case PAM_MAXTRIES: 833076ad2f8SDag-Erling Smørgrav if (type == PAM_ACCT_EXPIRED) 834333ee039SDag-Erling Smørgrav sshpam_account_status = 0; 835076ad2f8SDag-Erling Smørgrav if (type == PAM_MAXTRIES) 836076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(1); 837333ee039SDag-Erling Smørgrav /* FALLTHROUGH */ 838cf2b5f3bSDag-Erling Smørgrav case PAM_AUTH_ERR: 839333ee039SDag-Erling Smørgrav debug3("PAM: %s", pam_strerror(sshpam_handle, type)); 840b74df5b2SDag-Erling Smørgrav if (**prompts != NULL && strlen(**prompts) != 0) { 841b74df5b2SDag-Erling Smørgrav *info = **prompts; 842b74df5b2SDag-Erling Smørgrav **prompts = NULL; 843b74df5b2SDag-Erling Smørgrav *num = 0; 844b74df5b2SDag-Erling Smørgrav **echo_on = 0; 845b74df5b2SDag-Erling Smørgrav ctxt->pam_done = -1; 846e4a9863fSDag-Erling Smørgrav free(msg); 847b74df5b2SDag-Erling Smørgrav return 0; 848b74df5b2SDag-Erling Smørgrav } 849b74df5b2SDag-Erling Smørgrav /* FALLTHROUGH */ 850b74df5b2SDag-Erling Smørgrav case PAM_SUCCESS: 851cf2b5f3bSDag-Erling Smørgrav if (**prompts != NULL) { 852cf2b5f3bSDag-Erling Smørgrav /* drain any accumulated messages */ 8531ec0d754SDag-Erling Smørgrav debug("PAM: %s", **prompts); 854*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(loginmsg, **prompts, 855*190cef3dSDag-Erling Smørgrav strlen(**prompts))) != 0) 856*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 857*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 858e4a9863fSDag-Erling Smørgrav free(**prompts); 859cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 860cf2b5f3bSDag-Erling Smørgrav } 861cf2b5f3bSDag-Erling Smørgrav if (type == PAM_SUCCESS) { 862aa49c926SDag-Erling Smørgrav if (!sshpam_authctxt->valid || 863aa49c926SDag-Erling Smørgrav (sshpam_authctxt->pw->pw_uid == 0 && 864aa49c926SDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 865aa49c926SDag-Erling Smørgrav fatal("Internal error: PAM auth " 866aa49c926SDag-Erling Smørgrav "succeeded when it should have " 867aa49c926SDag-Erling Smørgrav "failed"); 868*190cef3dSDag-Erling Smørgrav import_environments(buffer); 869cf2b5f3bSDag-Erling Smørgrav *num = 0; 870cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 871cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = 1; 872e4a9863fSDag-Erling Smørgrav free(msg); 873cf2b5f3bSDag-Erling Smørgrav return (0); 874cf2b5f3bSDag-Erling Smørgrav } 875342b8b88SKurt Lidl BLACKLIST_NOTIFY(BLACKLIST_BAD_USER, 876342b8b88SKurt Lidl sshpam_authctxt->user); 8775962c0e9SDag-Erling Smørgrav error("PAM: %s for %s%.100s from %.100s", msg, 8785962c0e9SDag-Erling Smørgrav sshpam_authctxt->valid ? "" : "illegal user ", 8795962c0e9SDag-Erling Smørgrav sshpam_authctxt->user, 880076ad2f8SDag-Erling Smørgrav auth_get_canonical_hostname(ssh, options.use_dns)); 8811ec0d754SDag-Erling Smørgrav /* FALLTHROUGH */ 882cf2b5f3bSDag-Erling Smørgrav default: 883cf2b5f3bSDag-Erling Smørgrav *num = 0; 884cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 885e4a9863fSDag-Erling Smørgrav free(msg); 886cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = -1; 887cf2b5f3bSDag-Erling Smørgrav return (-1); 888cf2b5f3bSDag-Erling Smørgrav } 889cf2b5f3bSDag-Erling Smørgrav } 890cf2b5f3bSDag-Erling Smørgrav return (-1); 891cf2b5f3bSDag-Erling Smørgrav } 892cf2b5f3bSDag-Erling Smørgrav 893076ad2f8SDag-Erling Smørgrav /* 894076ad2f8SDag-Erling Smørgrav * Returns a junk password of identical length to that the user supplied. 895076ad2f8SDag-Erling Smørgrav * Used to mitigate timing attacks against crypt(3)/PAM stacks that 896076ad2f8SDag-Erling Smørgrav * vary processing time in proportion to password length. 897076ad2f8SDag-Erling Smørgrav */ 898076ad2f8SDag-Erling Smørgrav static char * 899076ad2f8SDag-Erling Smørgrav fake_password(const char *wire_password) 900076ad2f8SDag-Erling Smørgrav { 901076ad2f8SDag-Erling Smørgrav const char junk[] = "\b\n\r\177INCORRECT"; 902076ad2f8SDag-Erling Smørgrav char *ret = NULL; 903076ad2f8SDag-Erling Smørgrav size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; 904076ad2f8SDag-Erling Smørgrav 905076ad2f8SDag-Erling Smørgrav if (l >= INT_MAX) 906076ad2f8SDag-Erling Smørgrav fatal("%s: password length too long: %zu", __func__, l); 907076ad2f8SDag-Erling Smørgrav 908076ad2f8SDag-Erling Smørgrav ret = malloc(l + 1); 909d93a896eSDag-Erling Smørgrav if (ret == NULL) 910d93a896eSDag-Erling Smørgrav return NULL; 911076ad2f8SDag-Erling Smørgrav for (i = 0; i < l; i++) 912076ad2f8SDag-Erling Smørgrav ret[i] = junk[i % (sizeof(junk) - 1)]; 913076ad2f8SDag-Erling Smørgrav ret[i] = '\0'; 914076ad2f8SDag-Erling Smørgrav return ret; 915076ad2f8SDag-Erling Smørgrav } 916076ad2f8SDag-Erling Smørgrav 917cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */ 918cf2b5f3bSDag-Erling Smørgrav static int 919cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp) 920cf2b5f3bSDag-Erling Smørgrav { 921*190cef3dSDag-Erling Smørgrav struct sshbuf *buffer; 922cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 923076ad2f8SDag-Erling Smørgrav char *fake; 924*190cef3dSDag-Erling Smørgrav int r; 925cf2b5f3bSDag-Erling Smørgrav 926b74df5b2SDag-Erling Smørgrav debug2("PAM: %s entering, %u responses", __func__, num); 927cf2b5f3bSDag-Erling Smørgrav switch (ctxt->pam_done) { 928cf2b5f3bSDag-Erling Smørgrav case 1: 929cf2b5f3bSDag-Erling Smørgrav sshpam_authenticated = 1; 930cf2b5f3bSDag-Erling Smørgrav return (0); 931cf2b5f3bSDag-Erling Smørgrav case 0: 932cf2b5f3bSDag-Erling Smørgrav break; 933cf2b5f3bSDag-Erling Smørgrav default: 934cf2b5f3bSDag-Erling Smørgrav return (-1); 935cf2b5f3bSDag-Erling Smørgrav } 936cf2b5f3bSDag-Erling Smørgrav if (num != 1) { 937cf2b5f3bSDag-Erling Smørgrav error("PAM: expected one response, got %u", num); 938cf2b5f3bSDag-Erling Smørgrav return (-1); 939cf2b5f3bSDag-Erling Smørgrav } 940*190cef3dSDag-Erling Smørgrav if ((buffer = sshbuf_new()) == NULL) 941*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 942aa49c926SDag-Erling Smørgrav if (sshpam_authctxt->valid && 943aa49c926SDag-Erling Smørgrav (sshpam_authctxt->pw->pw_uid != 0 || 944*190cef3dSDag-Erling Smørgrav options.permit_root_login == PERMIT_YES)) { 945*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, *resp)) != 0) 946*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 947*190cef3dSDag-Erling Smørgrav } else { 948076ad2f8SDag-Erling Smørgrav fake = fake_password(*resp); 949*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(buffer, fake)) != 0) 950*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 951076ad2f8SDag-Erling Smørgrav free(fake); 952076ad2f8SDag-Erling Smørgrav } 953*190cef3dSDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, buffer) == -1) { 954*190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 9551ec0d754SDag-Erling Smørgrav return (-1); 9561ec0d754SDag-Erling Smørgrav } 957*190cef3dSDag-Erling Smørgrav sshbuf_free(buffer); 958cf2b5f3bSDag-Erling Smørgrav return (1); 959cf2b5f3bSDag-Erling Smørgrav } 960cf2b5f3bSDag-Erling Smørgrav 961cf2b5f3bSDag-Erling Smørgrav static void 962cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp) 963cf2b5f3bSDag-Erling Smørgrav { 964cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 965cf2b5f3bSDag-Erling Smørgrav 9661ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 9671ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(); 968e4a9863fSDag-Erling Smørgrav free(ctxt); 969cf2b5f3bSDag-Erling Smørgrav /* 970cf2b5f3bSDag-Erling Smørgrav * We don't call sshpam_cleanup() here because we may need the PAM 971cf2b5f3bSDag-Erling Smørgrav * handle at a later stage, e.g. when setting up a session. It's 972cf2b5f3bSDag-Erling Smørgrav * still on the cleanup list, so pam_end() *will* be called before 973cf2b5f3bSDag-Erling Smørgrav * the server process terminates. 974cf2b5f3bSDag-Erling Smørgrav */ 975cf2b5f3bSDag-Erling Smørgrav } 976cf2b5f3bSDag-Erling Smørgrav 977cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = { 978cf2b5f3bSDag-Erling Smørgrav "pam", 979cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx, 980cf2b5f3bSDag-Erling Smørgrav sshpam_query, 981cf2b5f3bSDag-Erling Smørgrav sshpam_respond, 982cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx 983cf2b5f3bSDag-Erling Smørgrav }; 984cf2b5f3bSDag-Erling Smørgrav 985cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = { 986cf2b5f3bSDag-Erling Smørgrav "pam", 987cf2b5f3bSDag-Erling Smørgrav mm_sshpam_init_ctx, 988cf2b5f3bSDag-Erling Smørgrav mm_sshpam_query, 989cf2b5f3bSDag-Erling Smørgrav mm_sshpam_respond, 990cf2b5f3bSDag-Erling Smørgrav mm_sshpam_free_ctx 991cf2b5f3bSDag-Erling Smørgrav }; 992cf2b5f3bSDag-Erling Smørgrav 993cf2b5f3bSDag-Erling Smørgrav /* 994cf2b5f3bSDag-Erling Smørgrav * This replaces auth-pam.c 995cf2b5f3bSDag-Erling Smørgrav */ 996cf2b5f3bSDag-Erling Smørgrav void 9975962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt) 998cf2b5f3bSDag-Erling Smørgrav { 999cf2b5f3bSDag-Erling Smørgrav if (!options.use_pam) 1000cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation requested when UsePAM=no"); 1001cf2b5f3bSDag-Erling Smørgrav 10025962c0e9SDag-Erling Smørgrav if (sshpam_init(authctxt) == -1) 1003cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation failed"); 1004cf2b5f3bSDag-Erling Smørgrav } 1005cf2b5f3bSDag-Erling Smørgrav 1006cf2b5f3bSDag-Erling Smørgrav void 1007cf2b5f3bSDag-Erling Smørgrav finish_pam(void) 1008cf2b5f3bSDag-Erling Smørgrav { 10091ec0d754SDag-Erling Smørgrav sshpam_cleanup(); 1010cf2b5f3bSDag-Erling Smørgrav } 1011cf2b5f3bSDag-Erling Smørgrav 10124f52dfbbSDag-Erling Smørgrav 1013cf2b5f3bSDag-Erling Smørgrav u_int 1014cf2b5f3bSDag-Erling Smørgrav do_pam_account(void) 1015cf2b5f3bSDag-Erling Smørgrav { 1016aa49c926SDag-Erling Smørgrav debug("%s: called", __func__); 10171ec0d754SDag-Erling Smørgrav if (sshpam_account_status != -1) 10181ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 10191ec0d754SDag-Erling Smørgrav 10204f52dfbbSDag-Erling Smørgrav expose_authinfo(__func__); 10214f52dfbbSDag-Erling Smørgrav 1022cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_acct_mgmt(sshpam_handle, 0); 1023aa49c926SDag-Erling Smørgrav debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, 1024aa49c926SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1025cf2b5f3bSDag-Erling Smørgrav 10261ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { 10271ec0d754SDag-Erling Smørgrav sshpam_account_status = 0; 10281ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 102909958426SBrian Feldman } 103009958426SBrian Feldman 10311ec0d754SDag-Erling Smørgrav if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 103221e764dfSDag-Erling Smørgrav sshpam_password_change_required(1); 103309958426SBrian Feldman 10341ec0d754SDag-Erling Smørgrav sshpam_account_status = 1; 10351ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 103609958426SBrian Feldman } 103709958426SBrian Feldman 1038cf2b5f3bSDag-Erling Smørgrav void 1039cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init) 104009958426SBrian Feldman { 1041cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 1042aa49c926SDag-Erling Smørgrav (const void *)&store_conv); 1043cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 1044cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 1045cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1046cf2b5f3bSDag-Erling Smørgrav if (init) { 1047cf2b5f3bSDag-Erling Smørgrav debug("PAM: establishing credentials"); 1048cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); 1049cf2b5f3bSDag-Erling Smørgrav } else { 1050cf2b5f3bSDag-Erling Smørgrav debug("PAM: reinitializing credentials"); 1051cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); 1052cf2b5f3bSDag-Erling Smørgrav } 1053cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) { 1054cf2b5f3bSDag-Erling Smørgrav sshpam_cred_established = 1; 1055989dd127SDag-Erling Smørgrav return; 1056cf2b5f3bSDag-Erling Smørgrav } 1057cf2b5f3bSDag-Erling Smørgrav if (sshpam_authenticated) 1058cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_setcred(): %s", 1059cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 10602c917d39SAlfred Perlstein else 1061cf2b5f3bSDag-Erling Smørgrav debug("PAM: pam_setcred(): %s", 1062cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 106309958426SBrian Feldman } 106409958426SBrian Feldman 1065cf2b5f3bSDag-Erling Smørgrav static int 1066d4ecd108SDag-Erling Smørgrav sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, 1067cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 106809958426SBrian Feldman { 1069cf2b5f3bSDag-Erling Smørgrav char input[PAM_MAX_MSG_SIZE]; 1070cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 1071f388f5efSDag-Erling Smørgrav int i; 1072f388f5efSDag-Erling Smørgrav 10731ec0d754SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 10741ec0d754SDag-Erling Smørgrav 1075cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 1076cf2b5f3bSDag-Erling Smørgrav 10771ec0d754SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) 1078cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 1079cf2b5f3bSDag-Erling Smørgrav 1080333ee039SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 1081cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 1082cf2b5f3bSDag-Erling Smørgrav 1083cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 1084cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 1085cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 1086cf2b5f3bSDag-Erling Smørgrav reply[i].resp = 1087cf2b5f3bSDag-Erling Smørgrav read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 1088cf2b5f3bSDag-Erling Smørgrav RP_ALLOW_STDIN); 1089cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 1090cf2b5f3bSDag-Erling Smørgrav break; 1091cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 10921ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 1093d4af9e69SDag-Erling Smørgrav if (fgets(input, sizeof input, stdin) == NULL) 1094d4af9e69SDag-Erling Smørgrav input[0] = '\0'; 109521e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(input)) == NULL) 109621e764dfSDag-Erling Smørgrav goto fail; 1097cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 1098cf2b5f3bSDag-Erling Smørgrav break; 1099cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 1100cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 11011ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 1102cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 1103cf2b5f3bSDag-Erling Smørgrav break; 1104cf2b5f3bSDag-Erling Smørgrav default: 1105cf2b5f3bSDag-Erling Smørgrav goto fail; 1106f388f5efSDag-Erling Smørgrav } 1107f388f5efSDag-Erling Smørgrav } 1108cf2b5f3bSDag-Erling Smørgrav *resp = reply; 1109cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 1110cf2b5f3bSDag-Erling Smørgrav 1111cf2b5f3bSDag-Erling Smørgrav fail: 1112cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 1113e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 1114cf2b5f3bSDag-Erling Smørgrav } 1115e4a9863fSDag-Erling Smørgrav free(reply); 1116cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 1117cf2b5f3bSDag-Erling Smørgrav } 1118f388f5efSDag-Erling Smørgrav 111921e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; 11201ec0d754SDag-Erling Smørgrav 1121cf2b5f3bSDag-Erling Smørgrav /* 1122cf2b5f3bSDag-Erling Smørgrav * XXX this should be done in the authentication phase, but ssh1 doesn't 1123cf2b5f3bSDag-Erling Smørgrav * support that 1124cf2b5f3bSDag-Erling Smørgrav */ 1125cf2b5f3bSDag-Erling Smørgrav void 1126cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void) 112709958426SBrian Feldman { 1128cf2b5f3bSDag-Erling Smørgrav if (use_privsep) 1129cf2b5f3bSDag-Erling Smørgrav fatal("Password expired (unable to change with privsep)"); 1130cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 11311ec0d754SDag-Erling Smørgrav (const void *)&tty_conv); 1132cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 1133cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 1134cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1135cf2b5f3bSDag-Erling Smørgrav debug("PAM: changing password"); 1136cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); 1137cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 1138cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_chauthtok(): %s", 1139cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 114009958426SBrian Feldman } 114109958426SBrian Feldman 11421ec0d754SDag-Erling Smørgrav void 114347dd1d1bSDag-Erling Smørgrav do_pam_session(struct ssh *ssh) 11441ec0d754SDag-Erling Smørgrav { 11451ec0d754SDag-Erling Smørgrav debug3("PAM: opening session"); 11464f52dfbbSDag-Erling Smørgrav 11474f52dfbbSDag-Erling Smørgrav expose_authinfo(__func__); 11484f52dfbbSDag-Erling Smørgrav 11491ec0d754SDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 11501ec0d754SDag-Erling Smørgrav (const void *)&store_conv); 11511ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 11521ec0d754SDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 11531ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 11541ec0d754SDag-Erling Smørgrav sshpam_err = pam_open_session(sshpam_handle, 0); 1155aa49c926SDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) 11561ec0d754SDag-Erling Smørgrav sshpam_session_open = 1; 1157aa49c926SDag-Erling Smørgrav else { 1158aa49c926SDag-Erling Smørgrav sshpam_session_open = 0; 115947dd1d1bSDag-Erling Smørgrav auth_restrict_session(ssh); 1160aa49c926SDag-Erling Smørgrav error("PAM: pam_open_session(): %s", 1161aa49c926SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1162aa49c926SDag-Erling Smørgrav } 1163aa49c926SDag-Erling Smørgrav 1164aa49c926SDag-Erling Smørgrav } 1165aa49c926SDag-Erling Smørgrav 1166aa49c926SDag-Erling Smørgrav int 1167aa49c926SDag-Erling Smørgrav is_pam_session_open(void) 1168aa49c926SDag-Erling Smørgrav { 1169aa49c926SDag-Erling Smørgrav return sshpam_session_open; 11701ec0d754SDag-Erling Smørgrav } 11711ec0d754SDag-Erling Smørgrav 1172cf2b5f3bSDag-Erling Smørgrav /* 1173cf2b5f3bSDag-Erling Smørgrav * Set a PAM environment string. We need to do this so that the session 1174cf2b5f3bSDag-Erling Smørgrav * modules can handle things like Kerberos/GSI credentials that appear 1175cf2b5f3bSDag-Erling Smørgrav * during the ssh authentication process. 1176cf2b5f3bSDag-Erling Smørgrav */ 1177cf2b5f3bSDag-Erling Smørgrav int 1178cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value) 117909958426SBrian Feldman { 1180cf2b5f3bSDag-Erling Smørgrav int ret = 1; 1181cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV 1182cf2b5f3bSDag-Erling Smørgrav char *compound; 1183cf2b5f3bSDag-Erling Smørgrav size_t len; 118409958426SBrian Feldman 1185cf2b5f3bSDag-Erling Smørgrav len = strlen(name) + strlen(value) + 2; 1186cf2b5f3bSDag-Erling Smørgrav compound = xmalloc(len); 118709958426SBrian Feldman 1188cf2b5f3bSDag-Erling Smørgrav snprintf(compound, len, "%s=%s", name, value); 1189cf2b5f3bSDag-Erling Smørgrav ret = pam_putenv(sshpam_handle, compound); 1190e4a9863fSDag-Erling Smørgrav free(compound); 1191cf2b5f3bSDag-Erling Smørgrav #endif 119209958426SBrian Feldman 1193cf2b5f3bSDag-Erling Smørgrav return (ret); 1194cf2b5f3bSDag-Erling Smørgrav } 119509958426SBrian Feldman 11961ec0d754SDag-Erling Smørgrav char ** 11971ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void) 1198cf2b5f3bSDag-Erling Smørgrav { 11991ec0d754SDag-Erling Smørgrav return sshpam_env; 1200cf2b5f3bSDag-Erling Smørgrav } 1201cf2b5f3bSDag-Erling Smørgrav 1202cf2b5f3bSDag-Erling Smørgrav char ** 1203cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void) 1204cf2b5f3bSDag-Erling Smørgrav { 1205cf2b5f3bSDag-Erling Smørgrav return (pam_getenvlist(sshpam_handle)); 1206cf2b5f3bSDag-Erling Smørgrav } 1207cf2b5f3bSDag-Erling Smørgrav 1208cf2b5f3bSDag-Erling Smørgrav void 1209cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env) 1210cf2b5f3bSDag-Erling Smørgrav { 1211cf2b5f3bSDag-Erling Smørgrav char **envp; 1212cf2b5f3bSDag-Erling Smørgrav 1213cf2b5f3bSDag-Erling Smørgrav if (env == NULL) 1214cf2b5f3bSDag-Erling Smørgrav return; 1215cf2b5f3bSDag-Erling Smørgrav 1216cf2b5f3bSDag-Erling Smørgrav for (envp = env; *envp; envp++) 1217e4a9863fSDag-Erling Smørgrav free(*envp); 1218e4a9863fSDag-Erling Smørgrav free(env); 121909958426SBrian Feldman } 122009958426SBrian Feldman 122121e764dfSDag-Erling Smørgrav /* 122221e764dfSDag-Erling Smørgrav * "Blind" conversation function for password authentication. Assumes that 122321e764dfSDag-Erling Smørgrav * echo-off prompts are for the password and stores messages for later 122421e764dfSDag-Erling Smørgrav * display. 122521e764dfSDag-Erling Smørgrav */ 122621e764dfSDag-Erling Smørgrav static int 1227d4ecd108SDag-Erling Smørgrav sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, 122821e764dfSDag-Erling Smørgrav struct pam_response **resp, void *data) 122921e764dfSDag-Erling Smørgrav { 123021e764dfSDag-Erling Smørgrav struct pam_response *reply; 1231*190cef3dSDag-Erling Smørgrav int r, i; 123221e764dfSDag-Erling Smørgrav size_t len; 123321e764dfSDag-Erling Smørgrav 123421e764dfSDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 123521e764dfSDag-Erling Smørgrav 123621e764dfSDag-Erling Smørgrav *resp = NULL; 123721e764dfSDag-Erling Smørgrav 123821e764dfSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 123921e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 124021e764dfSDag-Erling Smørgrav 1241d4af9e69SDag-Erling Smørgrav if ((reply = calloc(n, sizeof(*reply))) == NULL) 124221e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 124321e764dfSDag-Erling Smørgrav 124421e764dfSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 124521e764dfSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 124621e764dfSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 124721e764dfSDag-Erling Smørgrav if (sshpam_password == NULL) 124821e764dfSDag-Erling Smørgrav goto fail; 124921e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(sshpam_password)) == NULL) 125021e764dfSDag-Erling Smørgrav goto fail; 125121e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 125221e764dfSDag-Erling Smørgrav break; 125321e764dfSDag-Erling Smørgrav case PAM_ERROR_MSG: 125421e764dfSDag-Erling Smørgrav case PAM_TEXT_INFO: 125521e764dfSDag-Erling Smørgrav len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 125621e764dfSDag-Erling Smørgrav if (len > 0) { 1257*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(loginmsg, "%s\n", 1258*190cef3dSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg))) != 0) 1259*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1260*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 126121e764dfSDag-Erling Smørgrav } 126221e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup("")) == NULL) 126321e764dfSDag-Erling Smørgrav goto fail; 126421e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 126521e764dfSDag-Erling Smørgrav break; 126621e764dfSDag-Erling Smørgrav default: 126721e764dfSDag-Erling Smørgrav goto fail; 126821e764dfSDag-Erling Smørgrav } 126921e764dfSDag-Erling Smørgrav } 127021e764dfSDag-Erling Smørgrav *resp = reply; 127121e764dfSDag-Erling Smørgrav return (PAM_SUCCESS); 127221e764dfSDag-Erling Smørgrav 127321e764dfSDag-Erling Smørgrav fail: 127421e764dfSDag-Erling Smørgrav for(i = 0; i < n; i++) { 1275e4a9863fSDag-Erling Smørgrav free(reply[i].resp); 127621e764dfSDag-Erling Smørgrav } 1277e4a9863fSDag-Erling Smørgrav free(reply); 127821e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 127921e764dfSDag-Erling Smørgrav } 128021e764dfSDag-Erling Smørgrav 128121e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; 128221e764dfSDag-Erling Smørgrav 128321e764dfSDag-Erling Smørgrav /* 128421e764dfSDag-Erling Smørgrav * Attempt password authentication via PAM 128521e764dfSDag-Erling Smørgrav */ 128621e764dfSDag-Erling Smørgrav int 128721e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password) 128821e764dfSDag-Erling Smørgrav { 128921e764dfSDag-Erling Smørgrav int flags = (options.permit_empty_passwd == 0 ? 129021e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 1291076ad2f8SDag-Erling Smørgrav char *fake = NULL; 129221e764dfSDag-Erling Smørgrav 129321e764dfSDag-Erling Smørgrav if (!options.use_pam || sshpam_handle == NULL) 129421e764dfSDag-Erling Smørgrav fatal("PAM: %s called when PAM disabled or failed to " 129521e764dfSDag-Erling Smørgrav "initialise.", __func__); 129621e764dfSDag-Erling Smørgrav 129721e764dfSDag-Erling Smørgrav sshpam_password = password; 129821e764dfSDag-Erling Smørgrav sshpam_authctxt = authctxt; 129921e764dfSDag-Erling Smørgrav 130021e764dfSDag-Erling Smørgrav /* 130121e764dfSDag-Erling Smørgrav * If the user logging in is invalid, or is root but is not permitted 130221e764dfSDag-Erling Smørgrav * by PermitRootLogin, use an invalid password to prevent leaking 130321e764dfSDag-Erling Smørgrav * information via timing (eg if the PAM config has a delay on fail). 130421e764dfSDag-Erling Smørgrav */ 130521e764dfSDag-Erling Smørgrav if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && 130621e764dfSDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 1307076ad2f8SDag-Erling Smørgrav sshpam_password = fake = fake_password(password); 130821e764dfSDag-Erling Smørgrav 130921e764dfSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 131021e764dfSDag-Erling Smørgrav (const void *)&passwd_conv); 131121e764dfSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 131221e764dfSDag-Erling Smørgrav fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, 131321e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 131421e764dfSDag-Erling Smørgrav 131521e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 131621e764dfSDag-Erling Smørgrav sshpam_password = NULL; 1317076ad2f8SDag-Erling Smørgrav free(fake); 1318076ad2f8SDag-Erling Smørgrav if (sshpam_err == PAM_MAXTRIES) 1319076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(1); 132021e764dfSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && authctxt->valid) { 132121e764dfSDag-Erling Smørgrav debug("PAM: password authentication accepted for %.100s", 132221e764dfSDag-Erling Smørgrav authctxt->user); 132321e764dfSDag-Erling Smørgrav return 1; 132421e764dfSDag-Erling Smørgrav } else { 132521e764dfSDag-Erling Smørgrav debug("PAM: password authentication failed for %.100s: %s", 132621e764dfSDag-Erling Smørgrav authctxt->valid ? authctxt->user : "an illegal user", 132721e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 132821e764dfSDag-Erling Smørgrav return 0; 132921e764dfSDag-Erling Smørgrav } 133021e764dfSDag-Erling Smørgrav } 1331076ad2f8SDag-Erling Smørgrav 1332076ad2f8SDag-Erling Smørgrav int 1333076ad2f8SDag-Erling Smørgrav sshpam_get_maxtries_reached(void) 1334076ad2f8SDag-Erling Smørgrav { 1335076ad2f8SDag-Erling Smørgrav return sshpam_maxtries_reached; 1336076ad2f8SDag-Erling Smørgrav } 1337076ad2f8SDag-Erling Smørgrav 1338076ad2f8SDag-Erling Smørgrav void 1339076ad2f8SDag-Erling Smørgrav sshpam_set_maxtries_reached(int reached) 1340076ad2f8SDag-Erling Smørgrav { 1341076ad2f8SDag-Erling Smørgrav if (reached == 0 || sshpam_maxtries_reached) 1342076ad2f8SDag-Erling Smørgrav return; 1343076ad2f8SDag-Erling Smørgrav sshpam_maxtries_reached = 1; 1344076ad2f8SDag-Erling Smørgrav options.password_authentication = 0; 1345076ad2f8SDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1346076ad2f8SDag-Erling Smørgrav options.challenge_response_authentication = 0; 1347076ad2f8SDag-Erling Smørgrav } 134809958426SBrian Feldman #endif /* USE_PAM */ 1349