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 48cf2b5f3bSDag-Erling Smørgrav /* Based on $xFreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */ 4909958426SBrian Feldman #include "includes.h" 50b74df5b2SDag-Erling Smørgrav RCSID("$Id: auth-pam.c,v 1.128 2006/01/29 05:46:13 dtucker Exp $"); 51cf2b5f3bSDag-Erling Smørgrav RCSID("$FreeBSD$"); 5209958426SBrian Feldman 5309958426SBrian Feldman #ifdef USE_PAM 541ec0d754SDag-Erling Smørgrav #if defined(HAVE_SECURITY_PAM_APPL_H) 55cf2b5f3bSDag-Erling Smørgrav #include <security/pam_appl.h> 561ec0d754SDag-Erling Smørgrav #elif defined (HAVE_PAM_PAM_APPL_H) 571ec0d754SDag-Erling Smørgrav #include <pam/pam_appl.h> 581ec0d754SDag-Erling Smørgrav #endif 59cf2b5f3bSDag-Erling Smørgrav 60d4ecd108SDag-Erling Smørgrav /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ 61d4ecd108SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE 62d4ecd108SDag-Erling Smørgrav # define sshpam_const /* Solaris, HP-UX, AIX */ 63d4ecd108SDag-Erling Smørgrav #else 64d4ecd108SDag-Erling Smørgrav # define sshpam_const const /* LinuxPAM, OpenPAM */ 65d4ecd108SDag-Erling Smørgrav #endif 66d4ecd108SDag-Erling Smørgrav 67989dd127SDag-Erling Smørgrav #include "auth.h" 68989dd127SDag-Erling Smørgrav #include "auth-pam.h" 69cf2b5f3bSDag-Erling Smørgrav #include "buffer.h" 70cf2b5f3bSDag-Erling Smørgrav #include "bufaux.h" 714c5de869SBrian Feldman #include "canohost.h" 72cf2b5f3bSDag-Erling Smørgrav #include "log.h" 73cf2b5f3bSDag-Erling Smørgrav #include "monitor_wrap.h" 74cf2b5f3bSDag-Erling Smørgrav #include "msg.h" 75cf2b5f3bSDag-Erling Smørgrav #include "packet.h" 7621e764dfSDag-Erling Smørgrav #include "misc.h" 77cf2b5f3bSDag-Erling Smørgrav #include "servconf.h" 78cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h" 79cf2b5f3bSDag-Erling Smørgrav #include "xmalloc.h" 80cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h" 8109958426SBrian Feldman 82989dd127SDag-Erling Smørgrav extern ServerOptions options; 831ec0d754SDag-Erling Smørgrav extern Buffer loginmsg; 841ec0d754SDag-Erling Smørgrav extern int compat20; 855962c0e9SDag-Erling Smørgrav extern u_int utmp_len; 862c917d39SAlfred Perlstein 87aa49c926SDag-Erling Smørgrav /* so we don't silently change behaviour */ 88cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS 89aa49c926SDag-Erling Smørgrav # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK" 90aa49c926SDag-Erling Smørgrav #endif 91aa49c926SDag-Erling Smørgrav 92aa49c926SDag-Erling Smørgrav /* 93aa49c926SDag-Erling Smørgrav * Formerly known as USE_POSIX_THREADS, using this is completely unsupported 94aa49c926SDag-Erling Smørgrav * and generally a bad idea. Use at own risk and do not expect support if 95aa49c926SDag-Erling Smørgrav * this breaks. 96aa49c926SDag-Erling Smørgrav */ 97aa49c926SDag-Erling Smørgrav #ifdef UNSUPPORTED_POSIX_THREADS_HACK 98cf2b5f3bSDag-Erling Smørgrav #include <pthread.h> 99cf2b5f3bSDag-Erling Smørgrav /* 100cf2b5f3bSDag-Erling Smørgrav * Avoid namespace clash when *not* using pthreads for systems *with* 101cf2b5f3bSDag-Erling Smørgrav * pthreads, which unconditionally define pthread_t via sys/types.h 102cf2b5f3bSDag-Erling Smørgrav * (e.g. Linux) 103cf2b5f3bSDag-Erling Smørgrav */ 104cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t; 105cf2b5f3bSDag-Erling Smørgrav #else 1061ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t; 1071ec0d754SDag-Erling Smørgrav #endif 1081ec0d754SDag-Erling Smørgrav 1091ec0d754SDag-Erling Smørgrav struct pam_ctxt { 1101ec0d754SDag-Erling Smørgrav sp_pthread_t pam_thread; 1111ec0d754SDag-Erling Smørgrav int pam_psock; 1121ec0d754SDag-Erling Smørgrav int pam_csock; 1131ec0d754SDag-Erling Smørgrav int pam_done; 1141ec0d754SDag-Erling Smørgrav }; 1151ec0d754SDag-Erling Smørgrav 1161ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *); 1171ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt; 1181ec0d754SDag-Erling Smørgrav 119aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 120cf2b5f3bSDag-Erling Smørgrav /* 121cf2b5f3bSDag-Erling Smørgrav * Simulate threads with processes. 122cf2b5f3bSDag-Erling Smørgrav */ 1231ec0d754SDag-Erling Smørgrav 1241ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1; 1251ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig; 1261ec0d754SDag-Erling Smørgrav 1271ec0d754SDag-Erling Smørgrav static void 1281ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig) 1291ec0d754SDag-Erling Smørgrav { 13021e764dfSDag-Erling Smørgrav signal(SIGCHLD, SIG_DFL); 1311ec0d754SDag-Erling Smørgrav if (cleanup_ctxt == NULL) 1321ec0d754SDag-Erling Smørgrav return; /* handler called after PAM cleanup, shouldn't happen */ 13321e764dfSDag-Erling Smørgrav if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) 13421e764dfSDag-Erling Smørgrav <= 0) { 13521e764dfSDag-Erling Smørgrav /* PAM thread has not exitted, privsep slave must have */ 13621e764dfSDag-Erling Smørgrav kill(cleanup_ctxt->pam_thread, SIGTERM); 13721e764dfSDag-Erling Smørgrav if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) 13821e764dfSDag-Erling Smørgrav <= 0) 13921e764dfSDag-Erling Smørgrav return; /* could not wait */ 14021e764dfSDag-Erling Smørgrav } 1411ec0d754SDag-Erling Smørgrav if (WIFSIGNALED(sshpam_thread_status) && 1421ec0d754SDag-Erling Smørgrav WTERMSIG(sshpam_thread_status) == SIGTERM) 1431ec0d754SDag-Erling Smørgrav return; /* terminated by pthread_cancel */ 1441ec0d754SDag-Erling Smørgrav if (!WIFEXITED(sshpam_thread_status)) 1451ec0d754SDag-Erling Smørgrav fatal("PAM: authentication thread exited unexpectedly"); 1461ec0d754SDag-Erling Smørgrav if (WEXITSTATUS(sshpam_thread_status) != 0) 1471ec0d754SDag-Erling Smørgrav fatal("PAM: authentication thread exited uncleanly"); 1481ec0d754SDag-Erling Smørgrav } 14909958426SBrian Feldman 150cf2b5f3bSDag-Erling Smørgrav static void 151cf2b5f3bSDag-Erling Smørgrav pthread_exit(void *value __unused) 15209958426SBrian Feldman { 153cf2b5f3bSDag-Erling Smørgrav _exit(0); 15409958426SBrian Feldman } 15509958426SBrian Feldman 156cf2b5f3bSDag-Erling Smørgrav static int 157cf2b5f3bSDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr __unused, 158cf2b5f3bSDag-Erling Smørgrav void *(*thread_start)(void *), void *arg) 159cf2b5f3bSDag-Erling Smørgrav { 160cf2b5f3bSDag-Erling Smørgrav pid_t pid; 161d4ecd108SDag-Erling Smørgrav struct pam_ctxt *ctx = arg; 162cf2b5f3bSDag-Erling Smørgrav 1635962c0e9SDag-Erling Smørgrav sshpam_thread_status = -1; 164cf2b5f3bSDag-Erling Smørgrav switch ((pid = fork())) { 165cf2b5f3bSDag-Erling Smørgrav case -1: 166cf2b5f3bSDag-Erling Smørgrav error("fork(): %s", strerror(errno)); 167cf2b5f3bSDag-Erling Smørgrav return (-1); 168cf2b5f3bSDag-Erling Smørgrav case 0: 169d4ecd108SDag-Erling Smørgrav close(ctx->pam_psock); 170d4ecd108SDag-Erling Smørgrav ctx->pam_psock = -1; 171cf2b5f3bSDag-Erling Smørgrav thread_start(arg); 172cf2b5f3bSDag-Erling Smørgrav _exit(1); 173cf2b5f3bSDag-Erling Smørgrav default: 174cf2b5f3bSDag-Erling Smørgrav *thread = pid; 175d4ecd108SDag-Erling Smørgrav close(ctx->pam_csock); 176d4ecd108SDag-Erling Smørgrav ctx->pam_csock = -1; 1771ec0d754SDag-Erling Smørgrav sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); 178cf2b5f3bSDag-Erling Smørgrav return (0); 179cf2b5f3bSDag-Erling Smørgrav } 180cf2b5f3bSDag-Erling Smørgrav } 181cf2b5f3bSDag-Erling Smørgrav 182cf2b5f3bSDag-Erling Smørgrav static int 183cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread) 184cf2b5f3bSDag-Erling Smørgrav { 1851ec0d754SDag-Erling Smørgrav signal(SIGCHLD, sshpam_oldsig); 186cf2b5f3bSDag-Erling Smørgrav return (kill(thread, SIGTERM)); 187cf2b5f3bSDag-Erling Smørgrav } 188cf2b5f3bSDag-Erling Smørgrav 189cf2b5f3bSDag-Erling Smørgrav static int 190cf2b5f3bSDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value __unused) 191cf2b5f3bSDag-Erling Smørgrav { 192cf2b5f3bSDag-Erling Smørgrav int status; 193cf2b5f3bSDag-Erling Smørgrav 1941ec0d754SDag-Erling Smørgrav if (sshpam_thread_status != -1) 1951ec0d754SDag-Erling Smørgrav return (sshpam_thread_status); 1961ec0d754SDag-Erling Smørgrav signal(SIGCHLD, sshpam_oldsig); 197cf2b5f3bSDag-Erling Smørgrav waitpid(thread, &status, 0); 198cf2b5f3bSDag-Erling Smørgrav return (status); 199cf2b5f3bSDag-Erling Smørgrav } 200cf2b5f3bSDag-Erling Smørgrav #endif 201cf2b5f3bSDag-Erling Smørgrav 202cf2b5f3bSDag-Erling Smørgrav 203cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL; 204cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0; 205cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0; 206cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0; 207cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0; 2081ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1; 2091ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL; 2105962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL; 21121e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL; 212aa49c926SDag-Erling Smørgrav static char badpw[] = "\b\n\r\177INCORRECT"; 213cf2b5f3bSDag-Erling Smørgrav 2141ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */ 2151ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST 2161ec0d754SDag-Erling Smørgrav static char ** 2171ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh) 2181ec0d754SDag-Erling Smørgrav { 2191ec0d754SDag-Erling Smørgrav /* 2201ec0d754SDag-Erling Smørgrav * XXX - If necessary, we can still support envrionment passing 2211ec0d754SDag-Erling Smørgrav * for platforms without pam_getenvlist by searching for known 2221ec0d754SDag-Erling Smørgrav * env vars (e.g. KRB5CCNAME) from the PAM environment. 2231ec0d754SDag-Erling Smørgrav */ 2241ec0d754SDag-Erling Smørgrav return NULL; 2251ec0d754SDag-Erling Smørgrav } 2261ec0d754SDag-Erling Smørgrav #endif 227cf2b5f3bSDag-Erling Smørgrav 22821e764dfSDag-Erling Smørgrav /* 22921e764dfSDag-Erling Smørgrav * Some platforms, notably Solaris, do not enforce password complexity 23021e764dfSDag-Erling Smørgrav * rules during pam_chauthtok() if the real uid of the calling process 23121e764dfSDag-Erling Smørgrav * is 0, on the assumption that it's being called by "passwd" run by root. 23221e764dfSDag-Erling Smørgrav * This wraps pam_chauthtok and sets/restore the real uid so PAM will do 23321e764dfSDag-Erling Smørgrav * the right thing. 23421e764dfSDag-Erling Smørgrav */ 23521e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID 23621e764dfSDag-Erling Smørgrav static int 23721e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) 23821e764dfSDag-Erling Smørgrav { 23921e764dfSDag-Erling Smørgrav int result; 24021e764dfSDag-Erling Smørgrav 24121e764dfSDag-Erling Smørgrav if (sshpam_authctxt == NULL) 24221e764dfSDag-Erling Smørgrav fatal("PAM: sshpam_authctxt not initialized"); 24321e764dfSDag-Erling Smørgrav if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) 24421e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 24521e764dfSDag-Erling Smørgrav result = pam_chauthtok(pamh, flags); 24621e764dfSDag-Erling Smørgrav if (setreuid(0, -1) == -1) 24721e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 24821e764dfSDag-Erling Smørgrav return result; 24921e764dfSDag-Erling Smørgrav } 25021e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) 25121e764dfSDag-Erling Smørgrav #endif 25221e764dfSDag-Erling Smørgrav 2531ec0d754SDag-Erling Smørgrav void 25421e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd) 2551ec0d754SDag-Erling Smørgrav { 2561ec0d754SDag-Erling Smørgrav debug3("%s %d", __func__, reqd); 2575962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 2585962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 2595962c0e9SDag-Erling Smørgrav sshpam_authctxt->force_pwchange = reqd; 2601ec0d754SDag-Erling Smørgrav if (reqd) { 2611ec0d754SDag-Erling Smørgrav no_port_forwarding_flag |= 2; 2621ec0d754SDag-Erling Smørgrav no_agent_forwarding_flag |= 2; 2631ec0d754SDag-Erling Smørgrav no_x11_forwarding_flag |= 2; 2641ec0d754SDag-Erling Smørgrav } else { 2651ec0d754SDag-Erling Smørgrav no_port_forwarding_flag &= ~2; 2661ec0d754SDag-Erling Smørgrav no_agent_forwarding_flag &= ~2; 2671ec0d754SDag-Erling Smørgrav no_x11_forwarding_flag &= ~2; 2681ec0d754SDag-Erling Smørgrav } 2691ec0d754SDag-Erling Smørgrav } 2701ec0d754SDag-Erling Smørgrav 2711ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */ 2721ec0d754SDag-Erling Smørgrav static void 2731ec0d754SDag-Erling Smørgrav import_environments(Buffer *b) 2741ec0d754SDag-Erling Smørgrav { 2751ec0d754SDag-Erling Smørgrav char *env; 2761ec0d754SDag-Erling Smørgrav u_int i, num_env; 2771ec0d754SDag-Erling Smørgrav int err; 2781ec0d754SDag-Erling Smørgrav 2791ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 2801ec0d754SDag-Erling Smørgrav 281aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 2821ec0d754SDag-Erling Smørgrav /* Import variables set by do_pam_account */ 2831ec0d754SDag-Erling Smørgrav sshpam_account_status = buffer_get_int(b); 28421e764dfSDag-Erling Smørgrav sshpam_password_change_required(buffer_get_int(b)); 2851ec0d754SDag-Erling Smørgrav 2861ec0d754SDag-Erling Smørgrav /* Import environment from subprocess */ 2871ec0d754SDag-Erling Smørgrav num_env = buffer_get_int(b); 2881ec0d754SDag-Erling Smørgrav sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env)); 2891ec0d754SDag-Erling Smørgrav debug3("PAM: num env strings %d", num_env); 2901ec0d754SDag-Erling Smørgrav for(i = 0; i < num_env; i++) 2911ec0d754SDag-Erling Smørgrav sshpam_env[i] = buffer_get_string(b, NULL); 2921ec0d754SDag-Erling Smørgrav 2931ec0d754SDag-Erling Smørgrav sshpam_env[num_env] = NULL; 2941ec0d754SDag-Erling Smørgrav 2951ec0d754SDag-Erling Smørgrav /* Import PAM environment from subprocess */ 2961ec0d754SDag-Erling Smørgrav num_env = buffer_get_int(b); 2971ec0d754SDag-Erling Smørgrav debug("PAM: num PAM env strings %d", num_env); 2981ec0d754SDag-Erling Smørgrav for(i = 0; i < num_env; i++) { 2991ec0d754SDag-Erling Smørgrav env = buffer_get_string(b, NULL); 3001ec0d754SDag-Erling Smørgrav 3011ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV 3021ec0d754SDag-Erling Smørgrav /* Errors are not fatal here */ 3031ec0d754SDag-Erling Smørgrav if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { 3041ec0d754SDag-Erling Smørgrav error("PAM: pam_putenv: %s", 3051ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 3061ec0d754SDag-Erling Smørgrav } 3071ec0d754SDag-Erling Smørgrav #endif 3081ec0d754SDag-Erling Smørgrav } 3095962c0e9SDag-Erling Smørgrav #endif 3101ec0d754SDag-Erling Smørgrav } 311cf2b5f3bSDag-Erling Smørgrav 312cf2b5f3bSDag-Erling Smørgrav /* 313cf2b5f3bSDag-Erling Smørgrav * Conversation function for authentication thread. 314cf2b5f3bSDag-Erling Smørgrav */ 315cf2b5f3bSDag-Erling Smørgrav static int 316d4ecd108SDag-Erling Smørgrav sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, 317cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 318cf2b5f3bSDag-Erling Smørgrav { 319cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 320cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 321cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 322cf2b5f3bSDag-Erling Smørgrav int i; 323cf2b5f3bSDag-Erling Smørgrav 3241ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 325cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 326cf2b5f3bSDag-Erling Smørgrav 32721e764dfSDag-Erling Smørgrav if (data == NULL) { 32821e764dfSDag-Erling Smørgrav error("PAM: conversation function passed a null context"); 32921e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 33021e764dfSDag-Erling Smørgrav } 331cf2b5f3bSDag-Erling Smørgrav ctxt = data; 332cf2b5f3bSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 333cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 334cf2b5f3bSDag-Erling Smørgrav 335cf2b5f3bSDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 336cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 337cf2b5f3bSDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 338cf2b5f3bSDag-Erling Smørgrav 339cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 340cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 341cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 342cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 343cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 344cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3451ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3461ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3471ec0d754SDag-Erling Smørgrav goto fail; 3481ec0d754SDag-Erling Smørgrav if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 3491ec0d754SDag-Erling Smørgrav goto fail; 350cf2b5f3bSDag-Erling Smørgrav if (buffer_get_char(&buffer) != PAM_AUTHTOK) 351cf2b5f3bSDag-Erling Smørgrav goto fail; 352cf2b5f3bSDag-Erling Smørgrav reply[i].resp = buffer_get_string(&buffer, NULL); 35309958426SBrian Feldman break; 354cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 355cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 356cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3571ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3581ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3591ec0d754SDag-Erling Smørgrav goto fail; 3601ec0d754SDag-Erling Smørgrav if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 3611ec0d754SDag-Erling Smørgrav goto fail; 362cf2b5f3bSDag-Erling Smørgrav if (buffer_get_char(&buffer) != PAM_AUTHTOK) 363cf2b5f3bSDag-Erling Smørgrav goto fail; 364cf2b5f3bSDag-Erling Smørgrav reply[i].resp = buffer_get_string(&buffer, NULL); 365cf2b5f3bSDag-Erling Smørgrav break; 366cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 367cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 368cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3691ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3701ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3711ec0d754SDag-Erling Smørgrav goto fail; 372cf2b5f3bSDag-Erling Smørgrav break; 373cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 374cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 375cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3761ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3771ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3781ec0d754SDag-Erling Smørgrav goto fail; 379cf2b5f3bSDag-Erling Smørgrav break; 380cf2b5f3bSDag-Erling Smørgrav default: 381cf2b5f3bSDag-Erling Smørgrav goto fail; 382cf2b5f3bSDag-Erling Smørgrav } 383cf2b5f3bSDag-Erling Smørgrav buffer_clear(&buffer); 384cf2b5f3bSDag-Erling Smørgrav } 385cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 386cf2b5f3bSDag-Erling Smørgrav *resp = reply; 387cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 388cf2b5f3bSDag-Erling Smørgrav 389cf2b5f3bSDag-Erling Smørgrav fail: 390cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 391cf2b5f3bSDag-Erling Smørgrav if (reply[i].resp != NULL) 392cf2b5f3bSDag-Erling Smørgrav xfree(reply[i].resp); 393cf2b5f3bSDag-Erling Smørgrav } 394cf2b5f3bSDag-Erling Smørgrav xfree(reply); 395cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 396cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 397cf2b5f3bSDag-Erling Smørgrav } 398cf2b5f3bSDag-Erling Smørgrav 399cf2b5f3bSDag-Erling Smørgrav /* 400cf2b5f3bSDag-Erling Smørgrav * Authentication thread. 401cf2b5f3bSDag-Erling Smørgrav */ 402cf2b5f3bSDag-Erling Smørgrav static void * 403cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp) 404cf2b5f3bSDag-Erling Smørgrav { 405cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 406cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 407cf2b5f3bSDag-Erling Smørgrav struct pam_conv sshpam_conv; 40821e764dfSDag-Erling Smørgrav int flags = (options.permit_empty_passwd == 0 ? 40921e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 410aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 4111ec0d754SDag-Erling Smørgrav extern char **environ; 4121ec0d754SDag-Erling Smørgrav char **env_from_pam; 4131ec0d754SDag-Erling Smørgrav u_int i; 414cf2b5f3bSDag-Erling Smørgrav const char *pam_user; 415d4ecd108SDag-Erling Smørgrav const char **ptr_pam_user = &pam_user; 416cf2b5f3bSDag-Erling Smørgrav 417d4ecd108SDag-Erling Smørgrav pam_get_item(sshpam_handle, PAM_USER, 418d4ecd108SDag-Erling Smørgrav (sshpam_const void **)ptr_pam_user); 4191ec0d754SDag-Erling Smørgrav environ[0] = NULL; 42021e764dfSDag-Erling Smørgrav 42121e764dfSDag-Erling Smørgrav if (sshpam_authctxt != NULL) { 42221e764dfSDag-Erling Smørgrav setproctitle("%s [pam]", 42321e764dfSDag-Erling Smørgrav sshpam_authctxt->valid ? pam_user : "unknown"); 42421e764dfSDag-Erling Smørgrav } 425cf2b5f3bSDag-Erling Smørgrav #endif 426cf2b5f3bSDag-Erling Smørgrav 427cf2b5f3bSDag-Erling Smørgrav sshpam_conv.conv = sshpam_thread_conv; 428cf2b5f3bSDag-Erling Smørgrav sshpam_conv.appdata_ptr = ctxt; 429cf2b5f3bSDag-Erling Smørgrav 4305962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 4315962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 4325962c0e9SDag-Erling Smørgrav 433cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 434cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 435cf2b5f3bSDag-Erling Smørgrav (const void *)&sshpam_conv); 436cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 437cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 43821e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 439cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 440cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 4411ec0d754SDag-Erling Smørgrav 4421ec0d754SDag-Erling Smørgrav if (compat20) { 4431ec0d754SDag-Erling Smørgrav if (!do_pam_account()) 4441ec0d754SDag-Erling Smørgrav goto auth_fail; 4455962c0e9SDag-Erling Smørgrav if (sshpam_authctxt->force_pwchange) { 4461ec0d754SDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, 4471ec0d754SDag-Erling Smørgrav PAM_CHANGE_EXPIRED_AUTHTOK); 4481ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 4491ec0d754SDag-Erling Smørgrav goto auth_fail; 45021e764dfSDag-Erling Smørgrav sshpam_password_change_required(0); 4511ec0d754SDag-Erling Smørgrav } 4521ec0d754SDag-Erling Smørgrav } 4531ec0d754SDag-Erling Smørgrav 454cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, "OK"); 4551ec0d754SDag-Erling Smørgrav 456aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK 4571ec0d754SDag-Erling Smørgrav /* Export variables set by do_pam_account */ 4581ec0d754SDag-Erling Smørgrav buffer_put_int(&buffer, sshpam_account_status); 4595962c0e9SDag-Erling Smørgrav buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); 4601ec0d754SDag-Erling Smørgrav 4611ec0d754SDag-Erling Smørgrav /* Export any environment strings set in child */ 4621ec0d754SDag-Erling Smørgrav for(i = 0; environ[i] != NULL; i++) 4631ec0d754SDag-Erling Smørgrav ; /* Count */ 4641ec0d754SDag-Erling Smørgrav buffer_put_int(&buffer, i); 4651ec0d754SDag-Erling Smørgrav for(i = 0; environ[i] != NULL; i++) 4661ec0d754SDag-Erling Smørgrav buffer_put_cstring(&buffer, environ[i]); 4671ec0d754SDag-Erling Smørgrav 4681ec0d754SDag-Erling Smørgrav /* Export any environment strings set by PAM in child */ 4691ec0d754SDag-Erling Smørgrav env_from_pam = pam_getenvlist(sshpam_handle); 4701ec0d754SDag-Erling Smørgrav for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) 4711ec0d754SDag-Erling Smørgrav ; /* Count */ 4721ec0d754SDag-Erling Smørgrav buffer_put_int(&buffer, i); 4731ec0d754SDag-Erling Smørgrav for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) 4741ec0d754SDag-Erling Smørgrav buffer_put_cstring(&buffer, env_from_pam[i]); 475aa49c926SDag-Erling Smørgrav #endif /* UNSUPPORTED_POSIX_THREADS_HACK */ 4761ec0d754SDag-Erling Smørgrav 4771ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 478cf2b5f3bSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); 479cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 480cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 481cf2b5f3bSDag-Erling Smørgrav 482cf2b5f3bSDag-Erling Smørgrav auth_fail: 483cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 484cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 4851ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 486cf2b5f3bSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); 487cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 488cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 489cf2b5f3bSDag-Erling Smørgrav 490cf2b5f3bSDag-Erling Smørgrav return (NULL); /* Avoid warning for non-pthread case */ 491cf2b5f3bSDag-Erling Smørgrav } 492cf2b5f3bSDag-Erling Smørgrav 4931ec0d754SDag-Erling Smørgrav void 4941ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void) 495cf2b5f3bSDag-Erling Smørgrav { 4961ec0d754SDag-Erling Smørgrav struct pam_ctxt *ctxt = cleanup_ctxt; 497cf2b5f3bSDag-Erling Smørgrav 4981ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 4991ec0d754SDag-Erling Smørgrav if (ctxt != NULL && ctxt->pam_thread != 0) { 500cf2b5f3bSDag-Erling Smørgrav pthread_cancel(ctxt->pam_thread); 501cf2b5f3bSDag-Erling Smørgrav pthread_join(ctxt->pam_thread, NULL); 502cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_psock); 503cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_csock); 5041ec0d754SDag-Erling Smørgrav memset(ctxt, 0, sizeof(*ctxt)); 5051ec0d754SDag-Erling Smørgrav cleanup_ctxt = NULL; 5061ec0d754SDag-Erling Smørgrav } 507cf2b5f3bSDag-Erling Smørgrav } 508cf2b5f3bSDag-Erling Smørgrav 509cf2b5f3bSDag-Erling Smørgrav static int 510d4ecd108SDag-Erling Smørgrav sshpam_null_conv(int n, sshpam_const struct pam_message **msg, 511cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 512cf2b5f3bSDag-Erling Smørgrav { 5131ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 514cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 515cf2b5f3bSDag-Erling Smørgrav } 516cf2b5f3bSDag-Erling Smørgrav 517cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 518cf2b5f3bSDag-Erling Smørgrav 519aa49c926SDag-Erling Smørgrav static int 520d4ecd108SDag-Erling Smørgrav sshpam_store_conv(int n, sshpam_const struct pam_message **msg, 521aa49c926SDag-Erling Smørgrav struct pam_response **resp, void *data) 522aa49c926SDag-Erling Smørgrav { 523aa49c926SDag-Erling Smørgrav struct pam_response *reply; 524aa49c926SDag-Erling Smørgrav int i; 525aa49c926SDag-Erling Smørgrav size_t len; 526aa49c926SDag-Erling Smørgrav 527aa49c926SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 528aa49c926SDag-Erling Smørgrav *resp = NULL; 529aa49c926SDag-Erling Smørgrav 530aa49c926SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 531aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 532aa49c926SDag-Erling Smørgrav 533aa49c926SDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 534aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 535aa49c926SDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 536aa49c926SDag-Erling Smørgrav 537aa49c926SDag-Erling Smørgrav for (i = 0; i < n; ++i) { 538aa49c926SDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 539aa49c926SDag-Erling Smørgrav case PAM_ERROR_MSG: 540aa49c926SDag-Erling Smørgrav case PAM_TEXT_INFO: 541aa49c926SDag-Erling Smørgrav len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 542aa49c926SDag-Erling Smørgrav buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len); 543aa49c926SDag-Erling Smørgrav buffer_append(&loginmsg, "\n", 1 ); 544aa49c926SDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 545aa49c926SDag-Erling Smørgrav break; 546aa49c926SDag-Erling Smørgrav default: 547aa49c926SDag-Erling Smørgrav goto fail; 548aa49c926SDag-Erling Smørgrav } 549aa49c926SDag-Erling Smørgrav } 550aa49c926SDag-Erling Smørgrav *resp = reply; 551aa49c926SDag-Erling Smørgrav return (PAM_SUCCESS); 552aa49c926SDag-Erling Smørgrav 553aa49c926SDag-Erling Smørgrav fail: 554aa49c926SDag-Erling Smørgrav for(i = 0; i < n; i++) { 555aa49c926SDag-Erling Smørgrav if (reply[i].resp != NULL) 556aa49c926SDag-Erling Smørgrav xfree(reply[i].resp); 557aa49c926SDag-Erling Smørgrav } 558aa49c926SDag-Erling Smørgrav xfree(reply); 559aa49c926SDag-Erling Smørgrav return (PAM_CONV_ERR); 560aa49c926SDag-Erling Smørgrav } 561aa49c926SDag-Erling Smørgrav 562aa49c926SDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL }; 563aa49c926SDag-Erling Smørgrav 5641ec0d754SDag-Erling Smørgrav void 5651ec0d754SDag-Erling Smørgrav sshpam_cleanup(void) 566cf2b5f3bSDag-Erling Smørgrav { 567cf2b5f3bSDag-Erling Smørgrav debug("PAM: cleanup"); 568cf2b5f3bSDag-Erling Smørgrav if (sshpam_handle == NULL) 569cf2b5f3bSDag-Erling Smørgrav return; 570cf2b5f3bSDag-Erling Smørgrav pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); 571cf2b5f3bSDag-Erling Smørgrav if (sshpam_cred_established) { 572cf2b5f3bSDag-Erling Smørgrav pam_setcred(sshpam_handle, PAM_DELETE_CRED); 573cf2b5f3bSDag-Erling Smørgrav sshpam_cred_established = 0; 574cf2b5f3bSDag-Erling Smørgrav } 575cf2b5f3bSDag-Erling Smørgrav if (sshpam_session_open) { 576cf2b5f3bSDag-Erling Smørgrav pam_close_session(sshpam_handle, PAM_SILENT); 577cf2b5f3bSDag-Erling Smørgrav sshpam_session_open = 0; 578cf2b5f3bSDag-Erling Smørgrav } 5791ec0d754SDag-Erling Smørgrav sshpam_authenticated = 0; 580cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 581cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 582cf2b5f3bSDag-Erling Smørgrav } 583cf2b5f3bSDag-Erling Smørgrav 584cf2b5f3bSDag-Erling Smørgrav static int 5855962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt) 586cf2b5f3bSDag-Erling Smørgrav { 587cf2b5f3bSDag-Erling Smørgrav extern char *__progname; 5885962c0e9SDag-Erling Smørgrav const char *pam_rhost, *pam_user, *user = authctxt->user; 589d4ecd108SDag-Erling Smørgrav const char **ptr_pam_user = &pam_user; 590cf2b5f3bSDag-Erling Smørgrav 591cf2b5f3bSDag-Erling Smørgrav if (sshpam_handle != NULL) { 592cf2b5f3bSDag-Erling Smørgrav /* We already have a PAM context; check if the user matches */ 593cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_get_item(sshpam_handle, 594d4ecd108SDag-Erling Smørgrav PAM_USER, (sshpam_const void **)ptr_pam_user); 595cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 596cf2b5f3bSDag-Erling Smørgrav return (0); 597cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 598cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 599cf2b5f3bSDag-Erling Smørgrav } 600cf2b5f3bSDag-Erling Smørgrav debug("PAM: initializing for \"%s\"", user); 601cf2b5f3bSDag-Erling Smørgrav sshpam_err = 602aa49c926SDag-Erling Smørgrav pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); 6035962c0e9SDag-Erling Smørgrav sshpam_authctxt = authctxt; 6045962c0e9SDag-Erling Smørgrav 605cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 606cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 607cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 608cf2b5f3bSDag-Erling Smørgrav return (-1); 609cf2b5f3bSDag-Erling Smørgrav } 610cf2b5f3bSDag-Erling Smørgrav pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns); 611cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); 612cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); 613cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 614cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 615cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 616cf2b5f3bSDag-Erling Smørgrav return (-1); 617cf2b5f3bSDag-Erling Smørgrav } 618cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE 619cf2b5f3bSDag-Erling Smørgrav /* 620cf2b5f3bSDag-Erling Smørgrav * Some silly PAM modules (e.g. pam_time) require a TTY to operate. 621cf2b5f3bSDag-Erling Smørgrav * sshd doesn't set the tty until too late in the auth process and 622cf2b5f3bSDag-Erling Smørgrav * may not even set one (for tty-less connections) 623cf2b5f3bSDag-Erling Smørgrav */ 624cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_TTY to \"ssh\""); 625cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); 626cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 627cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 628cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 629cf2b5f3bSDag-Erling Smørgrav return (-1); 630cf2b5f3bSDag-Erling Smørgrav } 631cf2b5f3bSDag-Erling Smørgrav #endif 632cf2b5f3bSDag-Erling Smørgrav return (0); 633cf2b5f3bSDag-Erling Smørgrav } 634cf2b5f3bSDag-Erling Smørgrav 635cf2b5f3bSDag-Erling Smørgrav static void * 636cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt) 637cf2b5f3bSDag-Erling Smørgrav { 638cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 639cf2b5f3bSDag-Erling Smørgrav int socks[2]; 640cf2b5f3bSDag-Erling Smørgrav 6411ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 642cf2b5f3bSDag-Erling Smørgrav /* Refuse to start if we don't have PAM enabled */ 643cf2b5f3bSDag-Erling Smørgrav if (!options.use_pam) 644cf2b5f3bSDag-Erling Smørgrav return NULL; 645cf2b5f3bSDag-Erling Smørgrav 646cf2b5f3bSDag-Erling Smørgrav /* Initialize PAM */ 6475962c0e9SDag-Erling Smørgrav if (sshpam_init(authctxt) == -1) { 648cf2b5f3bSDag-Erling Smørgrav error("PAM: initialization failed"); 649cf2b5f3bSDag-Erling Smørgrav return (NULL); 650cf2b5f3bSDag-Erling Smørgrav } 651cf2b5f3bSDag-Erling Smørgrav 652cf2b5f3bSDag-Erling Smørgrav ctxt = xmalloc(sizeof *ctxt); 6531ec0d754SDag-Erling Smørgrav memset(ctxt, 0, sizeof(*ctxt)); 6541ec0d754SDag-Erling Smørgrav 655cf2b5f3bSDag-Erling Smørgrav /* Start the authentication thread */ 656cf2b5f3bSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 657cf2b5f3bSDag-Erling Smørgrav error("PAM: failed create sockets: %s", strerror(errno)); 658cf2b5f3bSDag-Erling Smørgrav xfree(ctxt); 659cf2b5f3bSDag-Erling Smørgrav return (NULL); 660cf2b5f3bSDag-Erling Smørgrav } 661cf2b5f3bSDag-Erling Smørgrav ctxt->pam_psock = socks[0]; 662cf2b5f3bSDag-Erling Smørgrav ctxt->pam_csock = socks[1]; 663cf2b5f3bSDag-Erling Smørgrav if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { 664cf2b5f3bSDag-Erling Smørgrav error("PAM: failed to start authentication thread: %s", 665cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 666cf2b5f3bSDag-Erling Smørgrav close(socks[0]); 667cf2b5f3bSDag-Erling Smørgrav close(socks[1]); 668cf2b5f3bSDag-Erling Smørgrav xfree(ctxt); 669cf2b5f3bSDag-Erling Smørgrav return (NULL); 670cf2b5f3bSDag-Erling Smørgrav } 6711ec0d754SDag-Erling Smørgrav cleanup_ctxt = ctxt; 672cf2b5f3bSDag-Erling Smørgrav return (ctxt); 673cf2b5f3bSDag-Erling Smørgrav } 674cf2b5f3bSDag-Erling Smørgrav 675cf2b5f3bSDag-Erling Smørgrav static int 676cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info, 677cf2b5f3bSDag-Erling Smørgrav u_int *num, char ***prompts, u_int **echo_on) 678cf2b5f3bSDag-Erling Smørgrav { 679cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 680cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 681cf2b5f3bSDag-Erling Smørgrav size_t plen; 682cf2b5f3bSDag-Erling Smørgrav u_char type; 683cf2b5f3bSDag-Erling Smørgrav char *msg; 684aa49c926SDag-Erling Smørgrav size_t len, mlen; 685cf2b5f3bSDag-Erling Smørgrav 6861ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 687cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 688cf2b5f3bSDag-Erling Smørgrav *name = xstrdup(""); 689cf2b5f3bSDag-Erling Smørgrav *info = xstrdup(""); 690cf2b5f3bSDag-Erling Smørgrav *prompts = xmalloc(sizeof(char *)); 691cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 692cf2b5f3bSDag-Erling Smørgrav plen = 0; 693cf2b5f3bSDag-Erling Smørgrav *echo_on = xmalloc(sizeof(u_int)); 694cf2b5f3bSDag-Erling Smørgrav while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { 695cf2b5f3bSDag-Erling Smørgrav type = buffer_get_char(&buffer); 696cf2b5f3bSDag-Erling Smørgrav msg = buffer_get_string(&buffer, NULL); 697aa49c926SDag-Erling Smørgrav mlen = strlen(msg); 698cf2b5f3bSDag-Erling Smørgrav switch (type) { 699cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 700cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 701cf2b5f3bSDag-Erling Smørgrav *num = 1; 702aa49c926SDag-Erling Smørgrav len = plen + mlen + 1; 703cf2b5f3bSDag-Erling Smørgrav **prompts = xrealloc(**prompts, len); 704aa49c926SDag-Erling Smørgrav strlcpy(**prompts + plen, msg, len - plen); 705aa49c926SDag-Erling Smørgrav plen += mlen; 706cf2b5f3bSDag-Erling Smørgrav **echo_on = (type == PAM_PROMPT_ECHO_ON); 707cf2b5f3bSDag-Erling Smørgrav xfree(msg); 708cf2b5f3bSDag-Erling Smørgrav return (0); 709cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 710cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 711cf2b5f3bSDag-Erling Smørgrav /* accumulate messages */ 712aa49c926SDag-Erling Smørgrav len = plen + mlen + 2; 713cf2b5f3bSDag-Erling Smørgrav **prompts = xrealloc(**prompts, len); 714aa49c926SDag-Erling Smørgrav strlcpy(**prompts + plen, msg, len - plen); 715aa49c926SDag-Erling Smørgrav plen += mlen; 716aa49c926SDag-Erling Smørgrav strlcat(**prompts + plen, "\n", len - plen); 717aa49c926SDag-Erling Smørgrav plen++; 718cf2b5f3bSDag-Erling Smørgrav xfree(msg); 719cf2b5f3bSDag-Erling Smørgrav break; 720cf2b5f3bSDag-Erling Smørgrav case PAM_AUTH_ERR: 721b74df5b2SDag-Erling Smørgrav debug3("PAM: PAM_AUTH_ERR"); 722b74df5b2SDag-Erling Smørgrav if (**prompts != NULL && strlen(**prompts) != 0) { 723b74df5b2SDag-Erling Smørgrav *info = **prompts; 724b74df5b2SDag-Erling Smørgrav **prompts = NULL; 725b74df5b2SDag-Erling Smørgrav *num = 0; 726b74df5b2SDag-Erling Smørgrav **echo_on = 0; 727b74df5b2SDag-Erling Smørgrav ctxt->pam_done = -1; 728b74df5b2SDag-Erling Smørgrav return 0; 729b74df5b2SDag-Erling Smørgrav } 730b74df5b2SDag-Erling Smørgrav /* FALLTHROUGH */ 731b74df5b2SDag-Erling Smørgrav case PAM_SUCCESS: 732cf2b5f3bSDag-Erling Smørgrav if (**prompts != NULL) { 733cf2b5f3bSDag-Erling Smørgrav /* drain any accumulated messages */ 7341ec0d754SDag-Erling Smørgrav debug("PAM: %s", **prompts); 7351ec0d754SDag-Erling Smørgrav buffer_append(&loginmsg, **prompts, 7361ec0d754SDag-Erling Smørgrav strlen(**prompts)); 737cf2b5f3bSDag-Erling Smørgrav xfree(**prompts); 738cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 739cf2b5f3bSDag-Erling Smørgrav } 740cf2b5f3bSDag-Erling Smørgrav if (type == PAM_SUCCESS) { 741aa49c926SDag-Erling Smørgrav if (!sshpam_authctxt->valid || 742aa49c926SDag-Erling Smørgrav (sshpam_authctxt->pw->pw_uid == 0 && 743aa49c926SDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 744aa49c926SDag-Erling Smørgrav fatal("Internal error: PAM auth " 745aa49c926SDag-Erling Smørgrav "succeeded when it should have " 746aa49c926SDag-Erling Smørgrav "failed"); 7471ec0d754SDag-Erling Smørgrav import_environments(&buffer); 748cf2b5f3bSDag-Erling Smørgrav *num = 0; 749cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 750cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = 1; 751cf2b5f3bSDag-Erling Smørgrav xfree(msg); 752cf2b5f3bSDag-Erling Smørgrav return (0); 753cf2b5f3bSDag-Erling Smørgrav } 7545962c0e9SDag-Erling Smørgrav error("PAM: %s for %s%.100s from %.100s", msg, 7555962c0e9SDag-Erling Smørgrav sshpam_authctxt->valid ? "" : "illegal user ", 7565962c0e9SDag-Erling Smørgrav sshpam_authctxt->user, 7575962c0e9SDag-Erling Smørgrav get_remote_name_or_ip(utmp_len, options.use_dns)); 7581ec0d754SDag-Erling Smørgrav /* FALLTHROUGH */ 759cf2b5f3bSDag-Erling Smørgrav default: 760cf2b5f3bSDag-Erling Smørgrav *num = 0; 761cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 762cf2b5f3bSDag-Erling Smørgrav xfree(msg); 763cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = -1; 764cf2b5f3bSDag-Erling Smørgrav return (-1); 765cf2b5f3bSDag-Erling Smørgrav } 766cf2b5f3bSDag-Erling Smørgrav } 767cf2b5f3bSDag-Erling Smørgrav return (-1); 768cf2b5f3bSDag-Erling Smørgrav } 769cf2b5f3bSDag-Erling Smørgrav 770cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */ 771cf2b5f3bSDag-Erling Smørgrav static int 772cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp) 773cf2b5f3bSDag-Erling Smørgrav { 774cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 775cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 776cf2b5f3bSDag-Erling Smørgrav 777b74df5b2SDag-Erling Smørgrav debug2("PAM: %s entering, %u responses", __func__, num); 778cf2b5f3bSDag-Erling Smørgrav switch (ctxt->pam_done) { 779cf2b5f3bSDag-Erling Smørgrav case 1: 780cf2b5f3bSDag-Erling Smørgrav sshpam_authenticated = 1; 781cf2b5f3bSDag-Erling Smørgrav return (0); 782cf2b5f3bSDag-Erling Smørgrav case 0: 783cf2b5f3bSDag-Erling Smørgrav break; 784cf2b5f3bSDag-Erling Smørgrav default: 785cf2b5f3bSDag-Erling Smørgrav return (-1); 786cf2b5f3bSDag-Erling Smørgrav } 787cf2b5f3bSDag-Erling Smørgrav if (num != 1) { 788cf2b5f3bSDag-Erling Smørgrav error("PAM: expected one response, got %u", num); 789cf2b5f3bSDag-Erling Smørgrav return (-1); 790cf2b5f3bSDag-Erling Smørgrav } 791cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 792aa49c926SDag-Erling Smørgrav if (sshpam_authctxt->valid && 793aa49c926SDag-Erling Smørgrav (sshpam_authctxt->pw->pw_uid != 0 || 794aa49c926SDag-Erling Smørgrav options.permit_root_login == PERMIT_YES)) 795cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, *resp); 796aa49c926SDag-Erling Smørgrav else 797aa49c926SDag-Erling Smørgrav buffer_put_cstring(&buffer, badpw); 7981ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { 7991ec0d754SDag-Erling Smørgrav buffer_free(&buffer); 8001ec0d754SDag-Erling Smørgrav return (-1); 8011ec0d754SDag-Erling Smørgrav } 802cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 803cf2b5f3bSDag-Erling Smørgrav return (1); 804cf2b5f3bSDag-Erling Smørgrav } 805cf2b5f3bSDag-Erling Smørgrav 806cf2b5f3bSDag-Erling Smørgrav static void 807cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp) 808cf2b5f3bSDag-Erling Smørgrav { 809cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 810cf2b5f3bSDag-Erling Smørgrav 8111ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 8121ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(); 813cf2b5f3bSDag-Erling Smørgrav xfree(ctxt); 814cf2b5f3bSDag-Erling Smørgrav /* 815cf2b5f3bSDag-Erling Smørgrav * We don't call sshpam_cleanup() here because we may need the PAM 816cf2b5f3bSDag-Erling Smørgrav * handle at a later stage, e.g. when setting up a session. It's 817cf2b5f3bSDag-Erling Smørgrav * still on the cleanup list, so pam_end() *will* be called before 818cf2b5f3bSDag-Erling Smørgrav * the server process terminates. 819cf2b5f3bSDag-Erling Smørgrav */ 820cf2b5f3bSDag-Erling Smørgrav } 821cf2b5f3bSDag-Erling Smørgrav 822cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = { 823cf2b5f3bSDag-Erling Smørgrav "pam", 824cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx, 825cf2b5f3bSDag-Erling Smørgrav sshpam_query, 826cf2b5f3bSDag-Erling Smørgrav sshpam_respond, 827cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx 828cf2b5f3bSDag-Erling Smørgrav }; 829cf2b5f3bSDag-Erling Smørgrav 830cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = { 831cf2b5f3bSDag-Erling Smørgrav "pam", 832cf2b5f3bSDag-Erling Smørgrav mm_sshpam_init_ctx, 833cf2b5f3bSDag-Erling Smørgrav mm_sshpam_query, 834cf2b5f3bSDag-Erling Smørgrav mm_sshpam_respond, 835cf2b5f3bSDag-Erling Smørgrav mm_sshpam_free_ctx 836cf2b5f3bSDag-Erling Smørgrav }; 837cf2b5f3bSDag-Erling Smørgrav 838cf2b5f3bSDag-Erling Smørgrav /* 839cf2b5f3bSDag-Erling Smørgrav * This replaces auth-pam.c 840cf2b5f3bSDag-Erling Smørgrav */ 841cf2b5f3bSDag-Erling Smørgrav void 8425962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt) 843cf2b5f3bSDag-Erling Smørgrav { 844cf2b5f3bSDag-Erling Smørgrav if (!options.use_pam) 845cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation requested when UsePAM=no"); 846cf2b5f3bSDag-Erling Smørgrav 8475962c0e9SDag-Erling Smørgrav if (sshpam_init(authctxt) == -1) 848cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation failed"); 849cf2b5f3bSDag-Erling Smørgrav } 850cf2b5f3bSDag-Erling Smørgrav 851cf2b5f3bSDag-Erling Smørgrav void 852cf2b5f3bSDag-Erling Smørgrav finish_pam(void) 853cf2b5f3bSDag-Erling Smørgrav { 8541ec0d754SDag-Erling Smørgrav sshpam_cleanup(); 855cf2b5f3bSDag-Erling Smørgrav } 856cf2b5f3bSDag-Erling Smørgrav 857cf2b5f3bSDag-Erling Smørgrav u_int 858cf2b5f3bSDag-Erling Smørgrav do_pam_account(void) 859cf2b5f3bSDag-Erling Smørgrav { 860aa49c926SDag-Erling Smørgrav debug("%s: called", __func__); 8611ec0d754SDag-Erling Smørgrav if (sshpam_account_status != -1) 8621ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 8631ec0d754SDag-Erling Smørgrav 864cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_acct_mgmt(sshpam_handle, 0); 865aa49c926SDag-Erling Smørgrav debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, 866aa49c926SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 867cf2b5f3bSDag-Erling Smørgrav 8681ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { 8691ec0d754SDag-Erling Smørgrav sshpam_account_status = 0; 8701ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 87109958426SBrian Feldman } 87209958426SBrian Feldman 8731ec0d754SDag-Erling Smørgrav if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 87421e764dfSDag-Erling Smørgrav sshpam_password_change_required(1); 87509958426SBrian Feldman 8761ec0d754SDag-Erling Smørgrav sshpam_account_status = 1; 8771ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 87809958426SBrian Feldman } 87909958426SBrian Feldman 880cf2b5f3bSDag-Erling Smørgrav void 881cf2b5f3bSDag-Erling Smørgrav do_pam_set_tty(const char *tty) 882cf2b5f3bSDag-Erling Smørgrav { 883cf2b5f3bSDag-Erling Smørgrav if (tty != NULL) { 884cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_TTY to \"%s\"", tty); 885cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty); 886cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 887cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_TTY: %s", 888cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 889cf2b5f3bSDag-Erling Smørgrav } 89009958426SBrian Feldman } 89109958426SBrian Feldman 892cf2b5f3bSDag-Erling Smørgrav void 893cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init) 89409958426SBrian Feldman { 895cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 896aa49c926SDag-Erling Smørgrav (const void *)&store_conv); 897cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 898cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 899cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 900cf2b5f3bSDag-Erling Smørgrav if (init) { 901cf2b5f3bSDag-Erling Smørgrav debug("PAM: establishing credentials"); 902cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); 903cf2b5f3bSDag-Erling Smørgrav } else { 904cf2b5f3bSDag-Erling Smørgrav debug("PAM: reinitializing credentials"); 905cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); 906cf2b5f3bSDag-Erling Smørgrav } 907cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) { 908cf2b5f3bSDag-Erling Smørgrav sshpam_cred_established = 1; 909989dd127SDag-Erling Smørgrav return; 910cf2b5f3bSDag-Erling Smørgrav } 911cf2b5f3bSDag-Erling Smørgrav if (sshpam_authenticated) 912cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_setcred(): %s", 913cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 9142c917d39SAlfred Perlstein else 915cf2b5f3bSDag-Erling Smørgrav debug("PAM: pam_setcred(): %s", 916cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 91709958426SBrian Feldman } 91809958426SBrian Feldman 919cf2b5f3bSDag-Erling Smørgrav static int 920d4ecd108SDag-Erling Smørgrav sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, 921cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 92209958426SBrian Feldman { 923cf2b5f3bSDag-Erling Smørgrav char input[PAM_MAX_MSG_SIZE]; 924cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 925f388f5efSDag-Erling Smørgrav int i; 926f388f5efSDag-Erling Smørgrav 9271ec0d754SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 9281ec0d754SDag-Erling Smørgrav 929cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 930cf2b5f3bSDag-Erling Smørgrav 9311ec0d754SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) 932cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 933cf2b5f3bSDag-Erling Smørgrav 934cf2b5f3bSDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 935cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 936cf2b5f3bSDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 937cf2b5f3bSDag-Erling Smørgrav 938cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 939cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 940cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 941cf2b5f3bSDag-Erling Smørgrav reply[i].resp = 942cf2b5f3bSDag-Erling Smørgrav read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 943cf2b5f3bSDag-Erling Smørgrav RP_ALLOW_STDIN); 944cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 945cf2b5f3bSDag-Erling Smørgrav break; 946cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 9471ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 948cf2b5f3bSDag-Erling Smørgrav fgets(input, sizeof input, stdin); 94921e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(input)) == NULL) 95021e764dfSDag-Erling Smørgrav goto fail; 951cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 952cf2b5f3bSDag-Erling Smørgrav break; 953cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 954cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 9551ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 956cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 957cf2b5f3bSDag-Erling Smørgrav break; 958cf2b5f3bSDag-Erling Smørgrav default: 959cf2b5f3bSDag-Erling Smørgrav goto fail; 960f388f5efSDag-Erling Smørgrav } 961f388f5efSDag-Erling Smørgrav } 962cf2b5f3bSDag-Erling Smørgrav *resp = reply; 963cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 964cf2b5f3bSDag-Erling Smørgrav 965cf2b5f3bSDag-Erling Smørgrav fail: 966cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 967cf2b5f3bSDag-Erling Smørgrav if (reply[i].resp != NULL) 968cf2b5f3bSDag-Erling Smørgrav xfree(reply[i].resp); 969cf2b5f3bSDag-Erling Smørgrav } 970cf2b5f3bSDag-Erling Smørgrav xfree(reply); 971cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 972cf2b5f3bSDag-Erling Smørgrav } 973f388f5efSDag-Erling Smørgrav 97421e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; 9751ec0d754SDag-Erling Smørgrav 976cf2b5f3bSDag-Erling Smørgrav /* 977cf2b5f3bSDag-Erling Smørgrav * XXX this should be done in the authentication phase, but ssh1 doesn't 978cf2b5f3bSDag-Erling Smørgrav * support that 979cf2b5f3bSDag-Erling Smørgrav */ 980cf2b5f3bSDag-Erling Smørgrav void 981cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void) 98209958426SBrian Feldman { 983cf2b5f3bSDag-Erling Smørgrav if (use_privsep) 984cf2b5f3bSDag-Erling Smørgrav fatal("Password expired (unable to change with privsep)"); 985cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 9861ec0d754SDag-Erling Smørgrav (const void *)&tty_conv); 987cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 988cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 989cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 990cf2b5f3bSDag-Erling Smørgrav debug("PAM: changing password"); 991cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); 992cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 993cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_chauthtok(): %s", 994cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 99509958426SBrian Feldman } 99609958426SBrian Feldman 9971ec0d754SDag-Erling Smørgrav void 9981ec0d754SDag-Erling Smørgrav do_pam_session(void) 9991ec0d754SDag-Erling Smørgrav { 10001ec0d754SDag-Erling Smørgrav debug3("PAM: opening session"); 10011ec0d754SDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 10021ec0d754SDag-Erling Smørgrav (const void *)&store_conv); 10031ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 10041ec0d754SDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 10051ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 10061ec0d754SDag-Erling Smørgrav sshpam_err = pam_open_session(sshpam_handle, 0); 1007aa49c926SDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) 10081ec0d754SDag-Erling Smørgrav sshpam_session_open = 1; 1009aa49c926SDag-Erling Smørgrav else { 1010aa49c926SDag-Erling Smørgrav sshpam_session_open = 0; 1011aa49c926SDag-Erling Smørgrav disable_forwarding(); 1012aa49c926SDag-Erling Smørgrav error("PAM: pam_open_session(): %s", 1013aa49c926SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 1014aa49c926SDag-Erling Smørgrav } 1015aa49c926SDag-Erling Smørgrav 1016aa49c926SDag-Erling Smørgrav } 1017aa49c926SDag-Erling Smørgrav 1018aa49c926SDag-Erling Smørgrav int 1019aa49c926SDag-Erling Smørgrav is_pam_session_open(void) 1020aa49c926SDag-Erling Smørgrav { 1021aa49c926SDag-Erling Smørgrav return sshpam_session_open; 10221ec0d754SDag-Erling Smørgrav } 10231ec0d754SDag-Erling Smørgrav 1024cf2b5f3bSDag-Erling Smørgrav /* 1025cf2b5f3bSDag-Erling Smørgrav * Set a PAM environment string. We need to do this so that the session 1026cf2b5f3bSDag-Erling Smørgrav * modules can handle things like Kerberos/GSI credentials that appear 1027cf2b5f3bSDag-Erling Smørgrav * during the ssh authentication process. 1028cf2b5f3bSDag-Erling Smørgrav */ 1029cf2b5f3bSDag-Erling Smørgrav int 1030cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value) 103109958426SBrian Feldman { 1032cf2b5f3bSDag-Erling Smørgrav int ret = 1; 1033cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV 1034cf2b5f3bSDag-Erling Smørgrav char *compound; 1035cf2b5f3bSDag-Erling Smørgrav size_t len; 103609958426SBrian Feldman 1037cf2b5f3bSDag-Erling Smørgrav len = strlen(name) + strlen(value) + 2; 1038cf2b5f3bSDag-Erling Smørgrav compound = xmalloc(len); 103909958426SBrian Feldman 1040cf2b5f3bSDag-Erling Smørgrav snprintf(compound, len, "%s=%s", name, value); 1041cf2b5f3bSDag-Erling Smørgrav ret = pam_putenv(sshpam_handle, compound); 1042cf2b5f3bSDag-Erling Smørgrav xfree(compound); 1043cf2b5f3bSDag-Erling Smørgrav #endif 104409958426SBrian Feldman 1045cf2b5f3bSDag-Erling Smørgrav return (ret); 1046cf2b5f3bSDag-Erling Smørgrav } 104709958426SBrian Feldman 10481ec0d754SDag-Erling Smørgrav char ** 10491ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void) 1050cf2b5f3bSDag-Erling Smørgrav { 10511ec0d754SDag-Erling Smørgrav return sshpam_env; 1052cf2b5f3bSDag-Erling Smørgrav } 1053cf2b5f3bSDag-Erling Smørgrav 1054cf2b5f3bSDag-Erling Smørgrav char ** 1055cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void) 1056cf2b5f3bSDag-Erling Smørgrav { 1057cf2b5f3bSDag-Erling Smørgrav return (pam_getenvlist(sshpam_handle)); 1058cf2b5f3bSDag-Erling Smørgrav } 1059cf2b5f3bSDag-Erling Smørgrav 1060cf2b5f3bSDag-Erling Smørgrav void 1061cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env) 1062cf2b5f3bSDag-Erling Smørgrav { 1063cf2b5f3bSDag-Erling Smørgrav char **envp; 1064cf2b5f3bSDag-Erling Smørgrav 1065cf2b5f3bSDag-Erling Smørgrav if (env == NULL) 1066cf2b5f3bSDag-Erling Smørgrav return; 1067cf2b5f3bSDag-Erling Smørgrav 1068cf2b5f3bSDag-Erling Smørgrav for (envp = env; *envp; envp++) 1069cf2b5f3bSDag-Erling Smørgrav xfree(*envp); 1070cf2b5f3bSDag-Erling Smørgrav xfree(env); 107109958426SBrian Feldman } 107209958426SBrian Feldman 107321e764dfSDag-Erling Smørgrav /* 107421e764dfSDag-Erling Smørgrav * "Blind" conversation function for password authentication. Assumes that 107521e764dfSDag-Erling Smørgrav * echo-off prompts are for the password and stores messages for later 107621e764dfSDag-Erling Smørgrav * display. 107721e764dfSDag-Erling Smørgrav */ 107821e764dfSDag-Erling Smørgrav static int 1079d4ecd108SDag-Erling Smørgrav sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, 108021e764dfSDag-Erling Smørgrav struct pam_response **resp, void *data) 108121e764dfSDag-Erling Smørgrav { 108221e764dfSDag-Erling Smørgrav struct pam_response *reply; 108321e764dfSDag-Erling Smørgrav int i; 108421e764dfSDag-Erling Smørgrav size_t len; 108521e764dfSDag-Erling Smørgrav 108621e764dfSDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 108721e764dfSDag-Erling Smørgrav 108821e764dfSDag-Erling Smørgrav *resp = NULL; 108921e764dfSDag-Erling Smørgrav 109021e764dfSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 109121e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 109221e764dfSDag-Erling Smørgrav 109321e764dfSDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 109421e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 109521e764dfSDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 109621e764dfSDag-Erling Smørgrav 109721e764dfSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 109821e764dfSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 109921e764dfSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 110021e764dfSDag-Erling Smørgrav if (sshpam_password == NULL) 110121e764dfSDag-Erling Smørgrav goto fail; 110221e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(sshpam_password)) == NULL) 110321e764dfSDag-Erling Smørgrav goto fail; 110421e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 110521e764dfSDag-Erling Smørgrav break; 110621e764dfSDag-Erling Smørgrav case PAM_ERROR_MSG: 110721e764dfSDag-Erling Smørgrav case PAM_TEXT_INFO: 110821e764dfSDag-Erling Smørgrav len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 110921e764dfSDag-Erling Smørgrav if (len > 0) { 111021e764dfSDag-Erling Smørgrav buffer_append(&loginmsg, 111121e764dfSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg), len); 111221e764dfSDag-Erling Smørgrav buffer_append(&loginmsg, "\n", 1); 111321e764dfSDag-Erling Smørgrav } 111421e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup("")) == NULL) 111521e764dfSDag-Erling Smørgrav goto fail; 111621e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 111721e764dfSDag-Erling Smørgrav break; 111821e764dfSDag-Erling Smørgrav default: 111921e764dfSDag-Erling Smørgrav goto fail; 112021e764dfSDag-Erling Smørgrav } 112121e764dfSDag-Erling Smørgrav } 112221e764dfSDag-Erling Smørgrav *resp = reply; 112321e764dfSDag-Erling Smørgrav return (PAM_SUCCESS); 112421e764dfSDag-Erling Smørgrav 112521e764dfSDag-Erling Smørgrav fail: 112621e764dfSDag-Erling Smørgrav for(i = 0; i < n; i++) { 112721e764dfSDag-Erling Smørgrav if (reply[i].resp != NULL) 112821e764dfSDag-Erling Smørgrav xfree(reply[i].resp); 112921e764dfSDag-Erling Smørgrav } 113021e764dfSDag-Erling Smørgrav xfree(reply); 113121e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 113221e764dfSDag-Erling Smørgrav } 113321e764dfSDag-Erling Smørgrav 113421e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; 113521e764dfSDag-Erling Smørgrav 113621e764dfSDag-Erling Smørgrav /* 113721e764dfSDag-Erling Smørgrav * Attempt password authentication via PAM 113821e764dfSDag-Erling Smørgrav */ 113921e764dfSDag-Erling Smørgrav int 114021e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password) 114121e764dfSDag-Erling Smørgrav { 114221e764dfSDag-Erling Smørgrav int flags = (options.permit_empty_passwd == 0 ? 114321e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 114421e764dfSDag-Erling Smørgrav 114521e764dfSDag-Erling Smørgrav if (!options.use_pam || sshpam_handle == NULL) 114621e764dfSDag-Erling Smørgrav fatal("PAM: %s called when PAM disabled or failed to " 114721e764dfSDag-Erling Smørgrav "initialise.", __func__); 114821e764dfSDag-Erling Smørgrav 114921e764dfSDag-Erling Smørgrav sshpam_password = password; 115021e764dfSDag-Erling Smørgrav sshpam_authctxt = authctxt; 115121e764dfSDag-Erling Smørgrav 115221e764dfSDag-Erling Smørgrav /* 115321e764dfSDag-Erling Smørgrav * If the user logging in is invalid, or is root but is not permitted 115421e764dfSDag-Erling Smørgrav * by PermitRootLogin, use an invalid password to prevent leaking 115521e764dfSDag-Erling Smørgrav * information via timing (eg if the PAM config has a delay on fail). 115621e764dfSDag-Erling Smørgrav */ 115721e764dfSDag-Erling Smørgrav if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && 115821e764dfSDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 115921e764dfSDag-Erling Smørgrav sshpam_password = badpw; 116021e764dfSDag-Erling Smørgrav 116121e764dfSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 116221e764dfSDag-Erling Smørgrav (const void *)&passwd_conv); 116321e764dfSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 116421e764dfSDag-Erling Smørgrav fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, 116521e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 116621e764dfSDag-Erling Smørgrav 116721e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 116821e764dfSDag-Erling Smørgrav sshpam_password = NULL; 116921e764dfSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && authctxt->valid) { 117021e764dfSDag-Erling Smørgrav debug("PAM: password authentication accepted for %.100s", 117121e764dfSDag-Erling Smørgrav authctxt->user); 117221e764dfSDag-Erling Smørgrav return 1; 117321e764dfSDag-Erling Smørgrav } else { 117421e764dfSDag-Erling Smørgrav debug("PAM: password authentication failed for %.100s: %s", 117521e764dfSDag-Erling Smørgrav authctxt->valid ? authctxt->user : "an illegal user", 117621e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 117721e764dfSDag-Erling Smørgrav return 0; 117821e764dfSDag-Erling Smørgrav } 117921e764dfSDag-Erling Smørgrav } 118009958426SBrian Feldman #endif /* USE_PAM */ 1181