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" 5021e764dfSDag-Erling Smørgrav RCSID("$Id: auth-pam.c,v 1.114 2004/08/16 13:12:06 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 60989dd127SDag-Erling Smørgrav #include "auth.h" 61989dd127SDag-Erling Smørgrav #include "auth-pam.h" 62cf2b5f3bSDag-Erling Smørgrav #include "buffer.h" 63cf2b5f3bSDag-Erling Smørgrav #include "bufaux.h" 644c5de869SBrian Feldman #include "canohost.h" 65cf2b5f3bSDag-Erling Smørgrav #include "log.h" 66cf2b5f3bSDag-Erling Smørgrav #include "monitor_wrap.h" 67cf2b5f3bSDag-Erling Smørgrav #include "msg.h" 68cf2b5f3bSDag-Erling Smørgrav #include "packet.h" 6921e764dfSDag-Erling Smørgrav #include "misc.h" 70cf2b5f3bSDag-Erling Smørgrav #include "servconf.h" 71cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h" 72cf2b5f3bSDag-Erling Smørgrav #include "xmalloc.h" 73cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h" 7409958426SBrian Feldman 75989dd127SDag-Erling Smørgrav extern ServerOptions options; 761ec0d754SDag-Erling Smørgrav extern Buffer loginmsg; 771ec0d754SDag-Erling Smørgrav extern int compat20; 785962c0e9SDag-Erling Smørgrav extern u_int utmp_len; 792c917d39SAlfred Perlstein 80cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS 81cf2b5f3bSDag-Erling Smørgrav #include <pthread.h> 82cf2b5f3bSDag-Erling Smørgrav /* 83cf2b5f3bSDag-Erling Smørgrav * Avoid namespace clash when *not* using pthreads for systems *with* 84cf2b5f3bSDag-Erling Smørgrav * pthreads, which unconditionally define pthread_t via sys/types.h 85cf2b5f3bSDag-Erling Smørgrav * (e.g. Linux) 86cf2b5f3bSDag-Erling Smørgrav */ 87cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t; 88cf2b5f3bSDag-Erling Smørgrav #else 891ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t; 901ec0d754SDag-Erling Smørgrav #endif 911ec0d754SDag-Erling Smørgrav 921ec0d754SDag-Erling Smørgrav struct pam_ctxt { 931ec0d754SDag-Erling Smørgrav sp_pthread_t pam_thread; 941ec0d754SDag-Erling Smørgrav int pam_psock; 951ec0d754SDag-Erling Smørgrav int pam_csock; 961ec0d754SDag-Erling Smørgrav int pam_done; 971ec0d754SDag-Erling Smørgrav }; 981ec0d754SDag-Erling Smørgrav 991ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *); 1001ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt; 1011ec0d754SDag-Erling Smørgrav 1021ec0d754SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS 103cf2b5f3bSDag-Erling Smørgrav /* 104cf2b5f3bSDag-Erling Smørgrav * Simulate threads with processes. 105cf2b5f3bSDag-Erling Smørgrav */ 1061ec0d754SDag-Erling Smørgrav 1071ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1; 1081ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig; 1091ec0d754SDag-Erling Smørgrav 1101ec0d754SDag-Erling Smørgrav static void 1111ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig) 1121ec0d754SDag-Erling Smørgrav { 11321e764dfSDag-Erling Smørgrav signal(SIGCHLD, SIG_DFL); 1141ec0d754SDag-Erling Smørgrav if (cleanup_ctxt == NULL) 1151ec0d754SDag-Erling Smørgrav return; /* handler called after PAM cleanup, shouldn't happen */ 11621e764dfSDag-Erling Smørgrav if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) 11721e764dfSDag-Erling Smørgrav <= 0) { 11821e764dfSDag-Erling Smørgrav /* PAM thread has not exitted, privsep slave must have */ 11921e764dfSDag-Erling Smørgrav kill(cleanup_ctxt->pam_thread, SIGTERM); 12021e764dfSDag-Erling Smørgrav if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) 12121e764dfSDag-Erling Smørgrav <= 0) 12221e764dfSDag-Erling Smørgrav return; /* could not wait */ 12321e764dfSDag-Erling Smørgrav } 1241ec0d754SDag-Erling Smørgrav if (WIFSIGNALED(sshpam_thread_status) && 1251ec0d754SDag-Erling Smørgrav WTERMSIG(sshpam_thread_status) == SIGTERM) 1261ec0d754SDag-Erling Smørgrav return; /* terminated by pthread_cancel */ 1271ec0d754SDag-Erling Smørgrav if (!WIFEXITED(sshpam_thread_status)) 1281ec0d754SDag-Erling Smørgrav fatal("PAM: authentication thread exited unexpectedly"); 1291ec0d754SDag-Erling Smørgrav if (WEXITSTATUS(sshpam_thread_status) != 0) 1301ec0d754SDag-Erling Smørgrav fatal("PAM: authentication thread exited uncleanly"); 1311ec0d754SDag-Erling Smørgrav } 13209958426SBrian Feldman 133cf2b5f3bSDag-Erling Smørgrav static void 134cf2b5f3bSDag-Erling Smørgrav pthread_exit(void *value __unused) 13509958426SBrian Feldman { 136cf2b5f3bSDag-Erling Smørgrav _exit(0); 13709958426SBrian Feldman } 13809958426SBrian Feldman 139cf2b5f3bSDag-Erling Smørgrav static int 140cf2b5f3bSDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr __unused, 141cf2b5f3bSDag-Erling Smørgrav void *(*thread_start)(void *), void *arg) 142cf2b5f3bSDag-Erling Smørgrav { 143cf2b5f3bSDag-Erling Smørgrav pid_t pid; 144cf2b5f3bSDag-Erling Smørgrav 1455962c0e9SDag-Erling Smørgrav sshpam_thread_status = -1; 146cf2b5f3bSDag-Erling Smørgrav switch ((pid = fork())) { 147cf2b5f3bSDag-Erling Smørgrav case -1: 148cf2b5f3bSDag-Erling Smørgrav error("fork(): %s", strerror(errno)); 149cf2b5f3bSDag-Erling Smørgrav return (-1); 150cf2b5f3bSDag-Erling Smørgrav case 0: 151cf2b5f3bSDag-Erling Smørgrav thread_start(arg); 152cf2b5f3bSDag-Erling Smørgrav _exit(1); 153cf2b5f3bSDag-Erling Smørgrav default: 154cf2b5f3bSDag-Erling Smørgrav *thread = pid; 1551ec0d754SDag-Erling Smørgrav sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); 156cf2b5f3bSDag-Erling Smørgrav return (0); 157cf2b5f3bSDag-Erling Smørgrav } 158cf2b5f3bSDag-Erling Smørgrav } 159cf2b5f3bSDag-Erling Smørgrav 160cf2b5f3bSDag-Erling Smørgrav static int 161cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread) 162cf2b5f3bSDag-Erling Smørgrav { 1631ec0d754SDag-Erling Smørgrav signal(SIGCHLD, sshpam_oldsig); 164cf2b5f3bSDag-Erling Smørgrav return (kill(thread, SIGTERM)); 165cf2b5f3bSDag-Erling Smørgrav } 166cf2b5f3bSDag-Erling Smørgrav 167cf2b5f3bSDag-Erling Smørgrav static int 168cf2b5f3bSDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value __unused) 169cf2b5f3bSDag-Erling Smørgrav { 170cf2b5f3bSDag-Erling Smørgrav int status; 171cf2b5f3bSDag-Erling Smørgrav 1721ec0d754SDag-Erling Smørgrav if (sshpam_thread_status != -1) 1731ec0d754SDag-Erling Smørgrav return (sshpam_thread_status); 1741ec0d754SDag-Erling Smørgrav signal(SIGCHLD, sshpam_oldsig); 175cf2b5f3bSDag-Erling Smørgrav waitpid(thread, &status, 0); 176cf2b5f3bSDag-Erling Smørgrav return (status); 177cf2b5f3bSDag-Erling Smørgrav } 178cf2b5f3bSDag-Erling Smørgrav #endif 179cf2b5f3bSDag-Erling Smørgrav 180cf2b5f3bSDag-Erling Smørgrav 181cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL; 182cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0; 183cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0; 184cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0; 185cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0; 1861ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1; 1871ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL; 1885962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL; 18921e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL; 190cf2b5f3bSDag-Erling Smørgrav 1911ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */ 1921ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST 1931ec0d754SDag-Erling Smørgrav static char ** 1941ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh) 1951ec0d754SDag-Erling Smørgrav { 1961ec0d754SDag-Erling Smørgrav /* 1971ec0d754SDag-Erling Smørgrav * XXX - If necessary, we can still support envrionment passing 1981ec0d754SDag-Erling Smørgrav * for platforms without pam_getenvlist by searching for known 1991ec0d754SDag-Erling Smørgrav * env vars (e.g. KRB5CCNAME) from the PAM environment. 2001ec0d754SDag-Erling Smørgrav */ 2011ec0d754SDag-Erling Smørgrav return NULL; 2021ec0d754SDag-Erling Smørgrav } 2031ec0d754SDag-Erling Smørgrav #endif 204cf2b5f3bSDag-Erling Smørgrav 20521e764dfSDag-Erling Smørgrav /* 20621e764dfSDag-Erling Smørgrav * Some platforms, notably Solaris, do not enforce password complexity 20721e764dfSDag-Erling Smørgrav * rules during pam_chauthtok() if the real uid of the calling process 20821e764dfSDag-Erling Smørgrav * is 0, on the assumption that it's being called by "passwd" run by root. 20921e764dfSDag-Erling Smørgrav * This wraps pam_chauthtok and sets/restore the real uid so PAM will do 21021e764dfSDag-Erling Smørgrav * the right thing. 21121e764dfSDag-Erling Smørgrav */ 21221e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID 21321e764dfSDag-Erling Smørgrav static int 21421e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) 21521e764dfSDag-Erling Smørgrav { 21621e764dfSDag-Erling Smørgrav int result; 21721e764dfSDag-Erling Smørgrav 21821e764dfSDag-Erling Smørgrav if (sshpam_authctxt == NULL) 21921e764dfSDag-Erling Smørgrav fatal("PAM: sshpam_authctxt not initialized"); 22021e764dfSDag-Erling Smørgrav if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) 22121e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 22221e764dfSDag-Erling Smørgrav result = pam_chauthtok(pamh, flags); 22321e764dfSDag-Erling Smørgrav if (setreuid(0, -1) == -1) 22421e764dfSDag-Erling Smørgrav fatal("%s: setreuid failed: %s", __func__, strerror(errno)); 22521e764dfSDag-Erling Smørgrav return result; 22621e764dfSDag-Erling Smørgrav } 22721e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) 22821e764dfSDag-Erling Smørgrav #endif 22921e764dfSDag-Erling Smørgrav 2301ec0d754SDag-Erling Smørgrav void 23121e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd) 2321ec0d754SDag-Erling Smørgrav { 2331ec0d754SDag-Erling Smørgrav debug3("%s %d", __func__, reqd); 2345962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 2355962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 2365962c0e9SDag-Erling Smørgrav sshpam_authctxt->force_pwchange = reqd; 2371ec0d754SDag-Erling Smørgrav if (reqd) { 2381ec0d754SDag-Erling Smørgrav no_port_forwarding_flag |= 2; 2391ec0d754SDag-Erling Smørgrav no_agent_forwarding_flag |= 2; 2401ec0d754SDag-Erling Smørgrav no_x11_forwarding_flag |= 2; 2411ec0d754SDag-Erling Smørgrav } else { 2421ec0d754SDag-Erling Smørgrav no_port_forwarding_flag &= ~2; 2431ec0d754SDag-Erling Smørgrav no_agent_forwarding_flag &= ~2; 2441ec0d754SDag-Erling Smørgrav no_x11_forwarding_flag &= ~2; 2451ec0d754SDag-Erling Smørgrav } 2461ec0d754SDag-Erling Smørgrav } 2471ec0d754SDag-Erling Smørgrav 2481ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */ 2491ec0d754SDag-Erling Smørgrav static void 2501ec0d754SDag-Erling Smørgrav import_environments(Buffer *b) 2511ec0d754SDag-Erling Smørgrav { 2521ec0d754SDag-Erling Smørgrav char *env; 2531ec0d754SDag-Erling Smørgrav u_int i, num_env; 2541ec0d754SDag-Erling Smørgrav int err; 2551ec0d754SDag-Erling Smørgrav 2561ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 2571ec0d754SDag-Erling Smørgrav 2585962c0e9SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS 2591ec0d754SDag-Erling Smørgrav /* Import variables set by do_pam_account */ 2601ec0d754SDag-Erling Smørgrav sshpam_account_status = buffer_get_int(b); 26121e764dfSDag-Erling Smørgrav sshpam_password_change_required(buffer_get_int(b)); 2621ec0d754SDag-Erling Smørgrav 2631ec0d754SDag-Erling Smørgrav /* Import environment from subprocess */ 2641ec0d754SDag-Erling Smørgrav num_env = buffer_get_int(b); 2651ec0d754SDag-Erling Smørgrav sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env)); 2661ec0d754SDag-Erling Smørgrav debug3("PAM: num env strings %d", num_env); 2671ec0d754SDag-Erling Smørgrav for(i = 0; i < num_env; i++) 2681ec0d754SDag-Erling Smørgrav sshpam_env[i] = buffer_get_string(b, NULL); 2691ec0d754SDag-Erling Smørgrav 2701ec0d754SDag-Erling Smørgrav sshpam_env[num_env] = NULL; 2711ec0d754SDag-Erling Smørgrav 2721ec0d754SDag-Erling Smørgrav /* Import PAM environment from subprocess */ 2731ec0d754SDag-Erling Smørgrav num_env = buffer_get_int(b); 2741ec0d754SDag-Erling Smørgrav debug("PAM: num PAM env strings %d", num_env); 2751ec0d754SDag-Erling Smørgrav for(i = 0; i < num_env; i++) { 2761ec0d754SDag-Erling Smørgrav env = buffer_get_string(b, NULL); 2771ec0d754SDag-Erling Smørgrav 2781ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV 2791ec0d754SDag-Erling Smørgrav /* Errors are not fatal here */ 2801ec0d754SDag-Erling Smørgrav if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { 2811ec0d754SDag-Erling Smørgrav error("PAM: pam_putenv: %s", 2821ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 2831ec0d754SDag-Erling Smørgrav } 2841ec0d754SDag-Erling Smørgrav #endif 2851ec0d754SDag-Erling Smørgrav } 2865962c0e9SDag-Erling Smørgrav #endif 2871ec0d754SDag-Erling Smørgrav } 288cf2b5f3bSDag-Erling Smørgrav 289cf2b5f3bSDag-Erling Smørgrav /* 290cf2b5f3bSDag-Erling Smørgrav * Conversation function for authentication thread. 291cf2b5f3bSDag-Erling Smørgrav */ 292cf2b5f3bSDag-Erling Smørgrav static int 29321e764dfSDag-Erling Smørgrav sshpam_thread_conv(int n, struct pam_message **msg, 294cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 295cf2b5f3bSDag-Erling Smørgrav { 296cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 297cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 298cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 299cf2b5f3bSDag-Erling Smørgrav int i; 300cf2b5f3bSDag-Erling Smørgrav 3011ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 302cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 303cf2b5f3bSDag-Erling Smørgrav 30421e764dfSDag-Erling Smørgrav if (data == NULL) { 30521e764dfSDag-Erling Smørgrav error("PAM: conversation function passed a null context"); 30621e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 30721e764dfSDag-Erling Smørgrav } 308cf2b5f3bSDag-Erling Smørgrav ctxt = data; 309cf2b5f3bSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 310cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 311cf2b5f3bSDag-Erling Smørgrav 312cf2b5f3bSDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 313cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 314cf2b5f3bSDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 315cf2b5f3bSDag-Erling Smørgrav 316cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 317cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 318cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 319cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 320cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 321cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3221ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3231ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3241ec0d754SDag-Erling Smørgrav goto fail; 3251ec0d754SDag-Erling Smørgrav if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 3261ec0d754SDag-Erling Smørgrav goto fail; 327cf2b5f3bSDag-Erling Smørgrav if (buffer_get_char(&buffer) != PAM_AUTHTOK) 328cf2b5f3bSDag-Erling Smørgrav goto fail; 329cf2b5f3bSDag-Erling Smørgrav reply[i].resp = buffer_get_string(&buffer, NULL); 33009958426SBrian Feldman break; 331cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 332cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 333cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3341ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3351ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3361ec0d754SDag-Erling Smørgrav goto fail; 3371ec0d754SDag-Erling Smørgrav if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) 3381ec0d754SDag-Erling Smørgrav goto fail; 339cf2b5f3bSDag-Erling Smørgrav if (buffer_get_char(&buffer) != PAM_AUTHTOK) 340cf2b5f3bSDag-Erling Smørgrav goto fail; 341cf2b5f3bSDag-Erling Smørgrav reply[i].resp = buffer_get_string(&buffer, NULL); 342cf2b5f3bSDag-Erling Smørgrav break; 343cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 344cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 345cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3461ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3471ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3481ec0d754SDag-Erling Smørgrav goto fail; 349cf2b5f3bSDag-Erling Smørgrav break; 350cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 351cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 352cf2b5f3bSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg)); 3531ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_csock, 3541ec0d754SDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) 3551ec0d754SDag-Erling Smørgrav goto fail; 356cf2b5f3bSDag-Erling Smørgrav break; 357cf2b5f3bSDag-Erling Smørgrav default: 358cf2b5f3bSDag-Erling Smørgrav goto fail; 359cf2b5f3bSDag-Erling Smørgrav } 360cf2b5f3bSDag-Erling Smørgrav buffer_clear(&buffer); 361cf2b5f3bSDag-Erling Smørgrav } 362cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 363cf2b5f3bSDag-Erling Smørgrav *resp = reply; 364cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 365cf2b5f3bSDag-Erling Smørgrav 366cf2b5f3bSDag-Erling Smørgrav fail: 367cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 368cf2b5f3bSDag-Erling Smørgrav if (reply[i].resp != NULL) 369cf2b5f3bSDag-Erling Smørgrav xfree(reply[i].resp); 370cf2b5f3bSDag-Erling Smørgrav } 371cf2b5f3bSDag-Erling Smørgrav xfree(reply); 372cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 373cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 374cf2b5f3bSDag-Erling Smørgrav } 375cf2b5f3bSDag-Erling Smørgrav 376cf2b5f3bSDag-Erling Smørgrav /* 377cf2b5f3bSDag-Erling Smørgrav * Authentication thread. 378cf2b5f3bSDag-Erling Smørgrav */ 379cf2b5f3bSDag-Erling Smørgrav static void * 380cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp) 381cf2b5f3bSDag-Erling Smørgrav { 382cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 383cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 384cf2b5f3bSDag-Erling Smørgrav struct pam_conv sshpam_conv; 38521e764dfSDag-Erling Smørgrav int flags = (options.permit_empty_passwd == 0 ? 38621e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 387cf2b5f3bSDag-Erling Smørgrav #ifndef USE_POSIX_THREADS 3881ec0d754SDag-Erling Smørgrav extern char **environ; 3891ec0d754SDag-Erling Smørgrav char **env_from_pam; 3901ec0d754SDag-Erling Smørgrav u_int i; 391cf2b5f3bSDag-Erling Smørgrav const char *pam_user; 392cf2b5f3bSDag-Erling Smørgrav 39321e764dfSDag-Erling Smørgrav pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user); 3941ec0d754SDag-Erling Smørgrav environ[0] = NULL; 39521e764dfSDag-Erling Smørgrav 39621e764dfSDag-Erling Smørgrav if (sshpam_authctxt != NULL) { 39721e764dfSDag-Erling Smørgrav setproctitle("%s [pam]", 39821e764dfSDag-Erling Smørgrav sshpam_authctxt->valid ? pam_user : "unknown"); 39921e764dfSDag-Erling Smørgrav } 400cf2b5f3bSDag-Erling Smørgrav #endif 401cf2b5f3bSDag-Erling Smørgrav 402cf2b5f3bSDag-Erling Smørgrav sshpam_conv.conv = sshpam_thread_conv; 403cf2b5f3bSDag-Erling Smørgrav sshpam_conv.appdata_ptr = ctxt; 404cf2b5f3bSDag-Erling Smørgrav 4055962c0e9SDag-Erling Smørgrav if (sshpam_authctxt == NULL) 4065962c0e9SDag-Erling Smørgrav fatal("%s: PAM authctxt not initialized", __func__); 4075962c0e9SDag-Erling Smørgrav 408cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 409cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 410cf2b5f3bSDag-Erling Smørgrav (const void *)&sshpam_conv); 411cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 412cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 41321e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 414cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 415cf2b5f3bSDag-Erling Smørgrav goto auth_fail; 4161ec0d754SDag-Erling Smørgrav 4171ec0d754SDag-Erling Smørgrav if (compat20) { 4181ec0d754SDag-Erling Smørgrav if (!do_pam_account()) 4191ec0d754SDag-Erling Smørgrav goto auth_fail; 4205962c0e9SDag-Erling Smørgrav if (sshpam_authctxt->force_pwchange) { 4211ec0d754SDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, 4221ec0d754SDag-Erling Smørgrav PAM_CHANGE_EXPIRED_AUTHTOK); 4231ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 4241ec0d754SDag-Erling Smørgrav goto auth_fail; 42521e764dfSDag-Erling Smørgrav sshpam_password_change_required(0); 4261ec0d754SDag-Erling Smørgrav } 4271ec0d754SDag-Erling Smørgrav } 4281ec0d754SDag-Erling Smørgrav 429cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, "OK"); 4301ec0d754SDag-Erling Smørgrav 4311ec0d754SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS 4321ec0d754SDag-Erling Smørgrav /* Export variables set by do_pam_account */ 4331ec0d754SDag-Erling Smørgrav buffer_put_int(&buffer, sshpam_account_status); 4345962c0e9SDag-Erling Smørgrav buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); 4351ec0d754SDag-Erling Smørgrav 4361ec0d754SDag-Erling Smørgrav /* Export any environment strings set in child */ 4371ec0d754SDag-Erling Smørgrav for(i = 0; environ[i] != NULL; i++) 4381ec0d754SDag-Erling Smørgrav ; /* Count */ 4391ec0d754SDag-Erling Smørgrav buffer_put_int(&buffer, i); 4401ec0d754SDag-Erling Smørgrav for(i = 0; environ[i] != NULL; i++) 4411ec0d754SDag-Erling Smørgrav buffer_put_cstring(&buffer, environ[i]); 4421ec0d754SDag-Erling Smørgrav 4431ec0d754SDag-Erling Smørgrav /* Export any environment strings set by PAM in child */ 4441ec0d754SDag-Erling Smørgrav env_from_pam = pam_getenvlist(sshpam_handle); 4451ec0d754SDag-Erling Smørgrav for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) 4461ec0d754SDag-Erling Smørgrav ; /* Count */ 4471ec0d754SDag-Erling Smørgrav buffer_put_int(&buffer, i); 4481ec0d754SDag-Erling Smørgrav for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) 4491ec0d754SDag-Erling Smørgrav buffer_put_cstring(&buffer, env_from_pam[i]); 4501ec0d754SDag-Erling Smørgrav #endif /* USE_POSIX_THREADS */ 4511ec0d754SDag-Erling Smørgrav 4521ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 453cf2b5f3bSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); 454cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 455cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 456cf2b5f3bSDag-Erling Smørgrav 457cf2b5f3bSDag-Erling Smørgrav auth_fail: 458cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, 459cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 4601ec0d754SDag-Erling Smørgrav /* XXX - can't do much about an error here */ 461cf2b5f3bSDag-Erling Smørgrav ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); 462cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 463cf2b5f3bSDag-Erling Smørgrav pthread_exit(NULL); 464cf2b5f3bSDag-Erling Smørgrav 465cf2b5f3bSDag-Erling Smørgrav return (NULL); /* Avoid warning for non-pthread case */ 466cf2b5f3bSDag-Erling Smørgrav } 467cf2b5f3bSDag-Erling Smørgrav 4681ec0d754SDag-Erling Smørgrav void 4691ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void) 470cf2b5f3bSDag-Erling Smørgrav { 4711ec0d754SDag-Erling Smørgrav struct pam_ctxt *ctxt = cleanup_ctxt; 472cf2b5f3bSDag-Erling Smørgrav 4731ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 4741ec0d754SDag-Erling Smørgrav if (ctxt != NULL && ctxt->pam_thread != 0) { 475cf2b5f3bSDag-Erling Smørgrav pthread_cancel(ctxt->pam_thread); 476cf2b5f3bSDag-Erling Smørgrav pthread_join(ctxt->pam_thread, NULL); 477cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_psock); 478cf2b5f3bSDag-Erling Smørgrav close(ctxt->pam_csock); 4791ec0d754SDag-Erling Smørgrav memset(ctxt, 0, sizeof(*ctxt)); 4801ec0d754SDag-Erling Smørgrav cleanup_ctxt = NULL; 4811ec0d754SDag-Erling Smørgrav } 482cf2b5f3bSDag-Erling Smørgrav } 483cf2b5f3bSDag-Erling Smørgrav 484cf2b5f3bSDag-Erling Smørgrav static int 48521e764dfSDag-Erling Smørgrav sshpam_null_conv(int n, struct pam_message **msg, 486cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 487cf2b5f3bSDag-Erling Smørgrav { 4881ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering, %d messages", __func__, n); 489cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 490cf2b5f3bSDag-Erling Smørgrav } 491cf2b5f3bSDag-Erling Smørgrav 492cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 493cf2b5f3bSDag-Erling Smørgrav 4941ec0d754SDag-Erling Smørgrav void 4951ec0d754SDag-Erling Smørgrav sshpam_cleanup(void) 496cf2b5f3bSDag-Erling Smørgrav { 497cf2b5f3bSDag-Erling Smørgrav debug("PAM: cleanup"); 498cf2b5f3bSDag-Erling Smørgrav if (sshpam_handle == NULL) 499cf2b5f3bSDag-Erling Smørgrav return; 500cf2b5f3bSDag-Erling Smørgrav pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); 501cf2b5f3bSDag-Erling Smørgrav if (sshpam_cred_established) { 502cf2b5f3bSDag-Erling Smørgrav pam_setcred(sshpam_handle, PAM_DELETE_CRED); 503cf2b5f3bSDag-Erling Smørgrav sshpam_cred_established = 0; 504cf2b5f3bSDag-Erling Smørgrav } 505cf2b5f3bSDag-Erling Smørgrav if (sshpam_session_open) { 506cf2b5f3bSDag-Erling Smørgrav pam_close_session(sshpam_handle, PAM_SILENT); 507cf2b5f3bSDag-Erling Smørgrav sshpam_session_open = 0; 508cf2b5f3bSDag-Erling Smørgrav } 5091ec0d754SDag-Erling Smørgrav sshpam_authenticated = 0; 510cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 511cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 512cf2b5f3bSDag-Erling Smørgrav } 513cf2b5f3bSDag-Erling Smørgrav 514cf2b5f3bSDag-Erling Smørgrav static int 5155962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt) 516cf2b5f3bSDag-Erling Smørgrav { 517cf2b5f3bSDag-Erling Smørgrav extern char *__progname; 5185962c0e9SDag-Erling Smørgrav const char *pam_rhost, *pam_user, *user = authctxt->user; 519cf2b5f3bSDag-Erling Smørgrav 520cf2b5f3bSDag-Erling Smørgrav if (sshpam_handle != NULL) { 521cf2b5f3bSDag-Erling Smørgrav /* We already have a PAM context; check if the user matches */ 522cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_get_item(sshpam_handle, 52321e764dfSDag-Erling Smørgrav PAM_USER, (void **)&pam_user); 524cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 525cf2b5f3bSDag-Erling Smørgrav return (0); 526cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 527cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 528cf2b5f3bSDag-Erling Smørgrav } 529cf2b5f3bSDag-Erling Smørgrav debug("PAM: initializing for \"%s\"", user); 530cf2b5f3bSDag-Erling Smørgrav sshpam_err = 531cf2b5f3bSDag-Erling Smørgrav pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle); 5325962c0e9SDag-Erling Smørgrav sshpam_authctxt = authctxt; 5335962c0e9SDag-Erling Smørgrav 534cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 535cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 536cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 537cf2b5f3bSDag-Erling Smørgrav return (-1); 538cf2b5f3bSDag-Erling Smørgrav } 539cf2b5f3bSDag-Erling Smørgrav pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns); 540cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); 541cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); 542cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 543cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 544cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 545cf2b5f3bSDag-Erling Smørgrav return (-1); 546cf2b5f3bSDag-Erling Smørgrav } 547cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE 548cf2b5f3bSDag-Erling Smørgrav /* 549cf2b5f3bSDag-Erling Smørgrav * Some silly PAM modules (e.g. pam_time) require a TTY to operate. 550cf2b5f3bSDag-Erling Smørgrav * sshd doesn't set the tty until too late in the auth process and 551cf2b5f3bSDag-Erling Smørgrav * may not even set one (for tty-less connections) 552cf2b5f3bSDag-Erling Smørgrav */ 553cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_TTY to \"ssh\""); 554cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); 555cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) { 556cf2b5f3bSDag-Erling Smørgrav pam_end(sshpam_handle, sshpam_err); 557cf2b5f3bSDag-Erling Smørgrav sshpam_handle = NULL; 558cf2b5f3bSDag-Erling Smørgrav return (-1); 559cf2b5f3bSDag-Erling Smørgrav } 560cf2b5f3bSDag-Erling Smørgrav #endif 561cf2b5f3bSDag-Erling Smørgrav return (0); 562cf2b5f3bSDag-Erling Smørgrav } 563cf2b5f3bSDag-Erling Smørgrav 564cf2b5f3bSDag-Erling Smørgrav static void * 565cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt) 566cf2b5f3bSDag-Erling Smørgrav { 567cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt; 568cf2b5f3bSDag-Erling Smørgrav int socks[2]; 569cf2b5f3bSDag-Erling Smørgrav 5701ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 571cf2b5f3bSDag-Erling Smørgrav /* Refuse to start if we don't have PAM enabled */ 572cf2b5f3bSDag-Erling Smørgrav if (!options.use_pam) 573cf2b5f3bSDag-Erling Smørgrav return NULL; 574cf2b5f3bSDag-Erling Smørgrav 575cf2b5f3bSDag-Erling Smørgrav /* Initialize PAM */ 5765962c0e9SDag-Erling Smørgrav if (sshpam_init(authctxt) == -1) { 577cf2b5f3bSDag-Erling Smørgrav error("PAM: initialization failed"); 578cf2b5f3bSDag-Erling Smørgrav return (NULL); 579cf2b5f3bSDag-Erling Smørgrav } 580cf2b5f3bSDag-Erling Smørgrav 581cf2b5f3bSDag-Erling Smørgrav ctxt = xmalloc(sizeof *ctxt); 5821ec0d754SDag-Erling Smørgrav memset(ctxt, 0, sizeof(*ctxt)); 5831ec0d754SDag-Erling Smørgrav 584cf2b5f3bSDag-Erling Smørgrav /* Start the authentication thread */ 585cf2b5f3bSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 586cf2b5f3bSDag-Erling Smørgrav error("PAM: failed create sockets: %s", strerror(errno)); 587cf2b5f3bSDag-Erling Smørgrav xfree(ctxt); 588cf2b5f3bSDag-Erling Smørgrav return (NULL); 589cf2b5f3bSDag-Erling Smørgrav } 590cf2b5f3bSDag-Erling Smørgrav ctxt->pam_psock = socks[0]; 591cf2b5f3bSDag-Erling Smørgrav ctxt->pam_csock = socks[1]; 592cf2b5f3bSDag-Erling Smørgrav if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { 593cf2b5f3bSDag-Erling Smørgrav error("PAM: failed to start authentication thread: %s", 594cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 595cf2b5f3bSDag-Erling Smørgrav close(socks[0]); 596cf2b5f3bSDag-Erling Smørgrav close(socks[1]); 597cf2b5f3bSDag-Erling Smørgrav xfree(ctxt); 598cf2b5f3bSDag-Erling Smørgrav return (NULL); 599cf2b5f3bSDag-Erling Smørgrav } 6001ec0d754SDag-Erling Smørgrav cleanup_ctxt = ctxt; 601cf2b5f3bSDag-Erling Smørgrav return (ctxt); 602cf2b5f3bSDag-Erling Smørgrav } 603cf2b5f3bSDag-Erling Smørgrav 604cf2b5f3bSDag-Erling Smørgrav static int 605cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info, 606cf2b5f3bSDag-Erling Smørgrav u_int *num, char ***prompts, u_int **echo_on) 607cf2b5f3bSDag-Erling Smørgrav { 608cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 609cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 610cf2b5f3bSDag-Erling Smørgrav size_t plen; 611cf2b5f3bSDag-Erling Smørgrav u_char type; 612cf2b5f3bSDag-Erling Smørgrav char *msg; 613cf2b5f3bSDag-Erling Smørgrav size_t len; 614cf2b5f3bSDag-Erling Smørgrav 6151ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 616cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 617cf2b5f3bSDag-Erling Smørgrav *name = xstrdup(""); 618cf2b5f3bSDag-Erling Smørgrav *info = xstrdup(""); 619cf2b5f3bSDag-Erling Smørgrav *prompts = xmalloc(sizeof(char *)); 620cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 621cf2b5f3bSDag-Erling Smørgrav plen = 0; 622cf2b5f3bSDag-Erling Smørgrav *echo_on = xmalloc(sizeof(u_int)); 623cf2b5f3bSDag-Erling Smørgrav while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { 624cf2b5f3bSDag-Erling Smørgrav type = buffer_get_char(&buffer); 625cf2b5f3bSDag-Erling Smørgrav msg = buffer_get_string(&buffer, NULL); 626cf2b5f3bSDag-Erling Smørgrav switch (type) { 627cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 628cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 629cf2b5f3bSDag-Erling Smørgrav *num = 1; 630cf2b5f3bSDag-Erling Smørgrav len = plen + strlen(msg) + 1; 631cf2b5f3bSDag-Erling Smørgrav **prompts = xrealloc(**prompts, len); 632cf2b5f3bSDag-Erling Smørgrav plen += snprintf(**prompts + plen, len, "%s", msg); 633cf2b5f3bSDag-Erling Smørgrav **echo_on = (type == PAM_PROMPT_ECHO_ON); 634cf2b5f3bSDag-Erling Smørgrav xfree(msg); 635cf2b5f3bSDag-Erling Smørgrav return (0); 636cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 637cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 638cf2b5f3bSDag-Erling Smørgrav /* accumulate messages */ 6391ec0d754SDag-Erling Smørgrav len = plen + strlen(msg) + 2; 640cf2b5f3bSDag-Erling Smørgrav **prompts = xrealloc(**prompts, len); 6411ec0d754SDag-Erling Smørgrav plen += snprintf(**prompts + plen, len, "%s\n", msg); 642cf2b5f3bSDag-Erling Smørgrav xfree(msg); 643cf2b5f3bSDag-Erling Smørgrav break; 644cf2b5f3bSDag-Erling Smørgrav case PAM_SUCCESS: 645cf2b5f3bSDag-Erling Smørgrav case PAM_AUTH_ERR: 646cf2b5f3bSDag-Erling Smørgrav if (**prompts != NULL) { 647cf2b5f3bSDag-Erling Smørgrav /* drain any accumulated messages */ 6481ec0d754SDag-Erling Smørgrav debug("PAM: %s", **prompts); 6491ec0d754SDag-Erling Smørgrav buffer_append(&loginmsg, **prompts, 6501ec0d754SDag-Erling Smørgrav strlen(**prompts)); 651cf2b5f3bSDag-Erling Smørgrav xfree(**prompts); 652cf2b5f3bSDag-Erling Smørgrav **prompts = NULL; 653cf2b5f3bSDag-Erling Smørgrav } 654cf2b5f3bSDag-Erling Smørgrav if (type == PAM_SUCCESS) { 6551ec0d754SDag-Erling Smørgrav import_environments(&buffer); 656cf2b5f3bSDag-Erling Smørgrav *num = 0; 657cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 658cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = 1; 659cf2b5f3bSDag-Erling Smørgrav xfree(msg); 660cf2b5f3bSDag-Erling Smørgrav return (0); 661cf2b5f3bSDag-Erling Smørgrav } 6625962c0e9SDag-Erling Smørgrav error("PAM: %s for %s%.100s from %.100s", msg, 6635962c0e9SDag-Erling Smørgrav sshpam_authctxt->valid ? "" : "illegal user ", 6645962c0e9SDag-Erling Smørgrav sshpam_authctxt->user, 6655962c0e9SDag-Erling Smørgrav get_remote_name_or_ip(utmp_len, options.use_dns)); 6661ec0d754SDag-Erling Smørgrav /* FALLTHROUGH */ 667cf2b5f3bSDag-Erling Smørgrav default: 668cf2b5f3bSDag-Erling Smørgrav *num = 0; 669cf2b5f3bSDag-Erling Smørgrav **echo_on = 0; 670cf2b5f3bSDag-Erling Smørgrav xfree(msg); 671cf2b5f3bSDag-Erling Smørgrav ctxt->pam_done = -1; 672cf2b5f3bSDag-Erling Smørgrav return (-1); 673cf2b5f3bSDag-Erling Smørgrav } 674cf2b5f3bSDag-Erling Smørgrav } 675cf2b5f3bSDag-Erling Smørgrav return (-1); 676cf2b5f3bSDag-Erling Smørgrav } 677cf2b5f3bSDag-Erling Smørgrav 678cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */ 679cf2b5f3bSDag-Erling Smørgrav static int 680cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp) 681cf2b5f3bSDag-Erling Smørgrav { 682cf2b5f3bSDag-Erling Smørgrav Buffer buffer; 683cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctx; 684cf2b5f3bSDag-Erling Smørgrav 6851ec0d754SDag-Erling Smørgrav debug2("PAM: %s entering, %d responses", __func__, num); 686cf2b5f3bSDag-Erling Smørgrav switch (ctxt->pam_done) { 687cf2b5f3bSDag-Erling Smørgrav case 1: 688cf2b5f3bSDag-Erling Smørgrav sshpam_authenticated = 1; 689cf2b5f3bSDag-Erling Smørgrav return (0); 690cf2b5f3bSDag-Erling Smørgrav case 0: 691cf2b5f3bSDag-Erling Smørgrav break; 692cf2b5f3bSDag-Erling Smørgrav default: 693cf2b5f3bSDag-Erling Smørgrav return (-1); 694cf2b5f3bSDag-Erling Smørgrav } 695cf2b5f3bSDag-Erling Smørgrav if (num != 1) { 696cf2b5f3bSDag-Erling Smørgrav error("PAM: expected one response, got %u", num); 697cf2b5f3bSDag-Erling Smørgrav return (-1); 698cf2b5f3bSDag-Erling Smørgrav } 699cf2b5f3bSDag-Erling Smørgrav buffer_init(&buffer); 700cf2b5f3bSDag-Erling Smørgrav buffer_put_cstring(&buffer, *resp); 7011ec0d754SDag-Erling Smørgrav if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { 7021ec0d754SDag-Erling Smørgrav buffer_free(&buffer); 7031ec0d754SDag-Erling Smørgrav return (-1); 7041ec0d754SDag-Erling Smørgrav } 705cf2b5f3bSDag-Erling Smørgrav buffer_free(&buffer); 706cf2b5f3bSDag-Erling Smørgrav return (1); 707cf2b5f3bSDag-Erling Smørgrav } 708cf2b5f3bSDag-Erling Smørgrav 709cf2b5f3bSDag-Erling Smørgrav static void 710cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp) 711cf2b5f3bSDag-Erling Smørgrav { 712cf2b5f3bSDag-Erling Smørgrav struct pam_ctxt *ctxt = ctxtp; 713cf2b5f3bSDag-Erling Smørgrav 7141ec0d754SDag-Erling Smørgrav debug3("PAM: %s entering", __func__); 7151ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(); 716cf2b5f3bSDag-Erling Smørgrav xfree(ctxt); 717cf2b5f3bSDag-Erling Smørgrav /* 718cf2b5f3bSDag-Erling Smørgrav * We don't call sshpam_cleanup() here because we may need the PAM 719cf2b5f3bSDag-Erling Smørgrav * handle at a later stage, e.g. when setting up a session. It's 720cf2b5f3bSDag-Erling Smørgrav * still on the cleanup list, so pam_end() *will* be called before 721cf2b5f3bSDag-Erling Smørgrav * the server process terminates. 722cf2b5f3bSDag-Erling Smørgrav */ 723cf2b5f3bSDag-Erling Smørgrav } 724cf2b5f3bSDag-Erling Smørgrav 725cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = { 726cf2b5f3bSDag-Erling Smørgrav "pam", 727cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx, 728cf2b5f3bSDag-Erling Smørgrav sshpam_query, 729cf2b5f3bSDag-Erling Smørgrav sshpam_respond, 730cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx 731cf2b5f3bSDag-Erling Smørgrav }; 732cf2b5f3bSDag-Erling Smørgrav 733cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = { 734cf2b5f3bSDag-Erling Smørgrav "pam", 735cf2b5f3bSDag-Erling Smørgrav mm_sshpam_init_ctx, 736cf2b5f3bSDag-Erling Smørgrav mm_sshpam_query, 737cf2b5f3bSDag-Erling Smørgrav mm_sshpam_respond, 738cf2b5f3bSDag-Erling Smørgrav mm_sshpam_free_ctx 739cf2b5f3bSDag-Erling Smørgrav }; 740cf2b5f3bSDag-Erling Smørgrav 741cf2b5f3bSDag-Erling Smørgrav /* 742cf2b5f3bSDag-Erling Smørgrav * This replaces auth-pam.c 743cf2b5f3bSDag-Erling Smørgrav */ 744cf2b5f3bSDag-Erling Smørgrav void 7455962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt) 746cf2b5f3bSDag-Erling Smørgrav { 747cf2b5f3bSDag-Erling Smørgrav if (!options.use_pam) 748cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation requested when UsePAM=no"); 749cf2b5f3bSDag-Erling Smørgrav 7505962c0e9SDag-Erling Smørgrav if (sshpam_init(authctxt) == -1) 751cf2b5f3bSDag-Erling Smørgrav fatal("PAM: initialisation failed"); 752cf2b5f3bSDag-Erling Smørgrav } 753cf2b5f3bSDag-Erling Smørgrav 754cf2b5f3bSDag-Erling Smørgrav void 755cf2b5f3bSDag-Erling Smørgrav finish_pam(void) 756cf2b5f3bSDag-Erling Smørgrav { 7571ec0d754SDag-Erling Smørgrav sshpam_cleanup(); 758cf2b5f3bSDag-Erling Smørgrav } 759cf2b5f3bSDag-Erling Smørgrav 760cf2b5f3bSDag-Erling Smørgrav u_int 761cf2b5f3bSDag-Erling Smørgrav do_pam_account(void) 762cf2b5f3bSDag-Erling Smørgrav { 7631ec0d754SDag-Erling Smørgrav if (sshpam_account_status != -1) 7641ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 7651ec0d754SDag-Erling Smørgrav 766cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_acct_mgmt(sshpam_handle, 0); 7671ec0d754SDag-Erling Smørgrav debug3("PAM: %s pam_acct_mgmt = %d", __func__, sshpam_err); 768cf2b5f3bSDag-Erling Smørgrav 7691ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { 7701ec0d754SDag-Erling Smørgrav sshpam_account_status = 0; 7711ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 77209958426SBrian Feldman } 77309958426SBrian Feldman 7741ec0d754SDag-Erling Smørgrav if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 77521e764dfSDag-Erling Smørgrav sshpam_password_change_required(1); 77609958426SBrian Feldman 7771ec0d754SDag-Erling Smørgrav sshpam_account_status = 1; 7781ec0d754SDag-Erling Smørgrav return (sshpam_account_status); 77909958426SBrian Feldman } 78009958426SBrian Feldman 781cf2b5f3bSDag-Erling Smørgrav void 782cf2b5f3bSDag-Erling Smørgrav do_pam_set_tty(const char *tty) 783cf2b5f3bSDag-Erling Smørgrav { 784cf2b5f3bSDag-Erling Smørgrav if (tty != NULL) { 785cf2b5f3bSDag-Erling Smørgrav debug("PAM: setting PAM_TTY to \"%s\"", tty); 786cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty); 787cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 788cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_TTY: %s", 789cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 790cf2b5f3bSDag-Erling Smørgrav } 79109958426SBrian Feldman } 79209958426SBrian Feldman 793cf2b5f3bSDag-Erling Smørgrav void 794cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init) 79509958426SBrian Feldman { 796cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 797cf2b5f3bSDag-Erling Smørgrav (const void *)&null_conv); 798cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 799cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 800cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 801cf2b5f3bSDag-Erling Smørgrav if (init) { 802cf2b5f3bSDag-Erling Smørgrav debug("PAM: establishing credentials"); 803cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); 804cf2b5f3bSDag-Erling Smørgrav } else { 805cf2b5f3bSDag-Erling Smørgrav debug("PAM: reinitializing credentials"); 806cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); 807cf2b5f3bSDag-Erling Smørgrav } 808cf2b5f3bSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS) { 809cf2b5f3bSDag-Erling Smørgrav sshpam_cred_established = 1; 810989dd127SDag-Erling Smørgrav return; 811cf2b5f3bSDag-Erling Smørgrav } 812cf2b5f3bSDag-Erling Smørgrav if (sshpam_authenticated) 813cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_setcred(): %s", 814cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 8152c917d39SAlfred Perlstein else 816cf2b5f3bSDag-Erling Smørgrav debug("PAM: pam_setcred(): %s", 817cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 81809958426SBrian Feldman } 81909958426SBrian Feldman 820cf2b5f3bSDag-Erling Smørgrav static int 82121e764dfSDag-Erling Smørgrav sshpam_tty_conv(int n, struct pam_message **msg, 822cf2b5f3bSDag-Erling Smørgrav struct pam_response **resp, void *data) 82309958426SBrian Feldman { 824cf2b5f3bSDag-Erling Smørgrav char input[PAM_MAX_MSG_SIZE]; 825cf2b5f3bSDag-Erling Smørgrav struct pam_response *reply; 826f388f5efSDag-Erling Smørgrav int i; 827f388f5efSDag-Erling Smørgrav 8281ec0d754SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 8291ec0d754SDag-Erling Smørgrav 830cf2b5f3bSDag-Erling Smørgrav *resp = NULL; 831cf2b5f3bSDag-Erling Smørgrav 8321ec0d754SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) 833cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 834cf2b5f3bSDag-Erling Smørgrav 835cf2b5f3bSDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 836cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 837cf2b5f3bSDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 838cf2b5f3bSDag-Erling Smørgrav 839cf2b5f3bSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 840cf2b5f3bSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 841cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 842cf2b5f3bSDag-Erling Smørgrav reply[i].resp = 843cf2b5f3bSDag-Erling Smørgrav read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 844cf2b5f3bSDag-Erling Smørgrav RP_ALLOW_STDIN); 845cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 846cf2b5f3bSDag-Erling Smørgrav break; 847cf2b5f3bSDag-Erling Smørgrav case PAM_PROMPT_ECHO_ON: 8481ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 849cf2b5f3bSDag-Erling Smørgrav fgets(input, sizeof input, stdin); 85021e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(input)) == NULL) 85121e764dfSDag-Erling Smørgrav goto fail; 852cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 853cf2b5f3bSDag-Erling Smørgrav break; 854cf2b5f3bSDag-Erling Smørgrav case PAM_ERROR_MSG: 855cf2b5f3bSDag-Erling Smørgrav case PAM_TEXT_INFO: 8561ec0d754SDag-Erling Smørgrav fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 857cf2b5f3bSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 858cf2b5f3bSDag-Erling Smørgrav break; 859cf2b5f3bSDag-Erling Smørgrav default: 860cf2b5f3bSDag-Erling Smørgrav goto fail; 861f388f5efSDag-Erling Smørgrav } 862f388f5efSDag-Erling Smørgrav } 863cf2b5f3bSDag-Erling Smørgrav *resp = reply; 864cf2b5f3bSDag-Erling Smørgrav return (PAM_SUCCESS); 865cf2b5f3bSDag-Erling Smørgrav 866cf2b5f3bSDag-Erling Smørgrav fail: 867cf2b5f3bSDag-Erling Smørgrav for(i = 0; i < n; i++) { 868cf2b5f3bSDag-Erling Smørgrav if (reply[i].resp != NULL) 869cf2b5f3bSDag-Erling Smørgrav xfree(reply[i].resp); 870cf2b5f3bSDag-Erling Smørgrav } 871cf2b5f3bSDag-Erling Smørgrav xfree(reply); 872cf2b5f3bSDag-Erling Smørgrav return (PAM_CONV_ERR); 873cf2b5f3bSDag-Erling Smørgrav } 874f388f5efSDag-Erling Smørgrav 87521e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; 8761ec0d754SDag-Erling Smørgrav 877cf2b5f3bSDag-Erling Smørgrav /* 878cf2b5f3bSDag-Erling Smørgrav * XXX this should be done in the authentication phase, but ssh1 doesn't 879cf2b5f3bSDag-Erling Smørgrav * support that 880cf2b5f3bSDag-Erling Smørgrav */ 881cf2b5f3bSDag-Erling Smørgrav void 882cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void) 88309958426SBrian Feldman { 884cf2b5f3bSDag-Erling Smørgrav if (use_privsep) 885cf2b5f3bSDag-Erling Smørgrav fatal("Password expired (unable to change with privsep)"); 886cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 8871ec0d754SDag-Erling Smørgrav (const void *)&tty_conv); 888cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 889cf2b5f3bSDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 890cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 891cf2b5f3bSDag-Erling Smørgrav debug("PAM: changing password"); 892cf2b5f3bSDag-Erling Smørgrav sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); 893cf2b5f3bSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 894cf2b5f3bSDag-Erling Smørgrav fatal("PAM: pam_chauthtok(): %s", 895cf2b5f3bSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 89609958426SBrian Feldman } 89709958426SBrian Feldman 8981ec0d754SDag-Erling Smørgrav static int 89921e764dfSDag-Erling Smørgrav sshpam_store_conv(int n, struct pam_message **msg, 9001ec0d754SDag-Erling Smørgrav struct pam_response **resp, void *data) 9011ec0d754SDag-Erling Smørgrav { 9021ec0d754SDag-Erling Smørgrav struct pam_response *reply; 9031ec0d754SDag-Erling Smørgrav int i; 9041ec0d754SDag-Erling Smørgrav size_t len; 9051ec0d754SDag-Erling Smørgrav 9061ec0d754SDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 9071ec0d754SDag-Erling Smørgrav *resp = NULL; 9081ec0d754SDag-Erling Smørgrav 9091ec0d754SDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 9101ec0d754SDag-Erling Smørgrav return (PAM_CONV_ERR); 9111ec0d754SDag-Erling Smørgrav 9121ec0d754SDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 9131ec0d754SDag-Erling Smørgrav return (PAM_CONV_ERR); 9141ec0d754SDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 9151ec0d754SDag-Erling Smørgrav 9161ec0d754SDag-Erling Smørgrav for (i = 0; i < n; ++i) { 9171ec0d754SDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 9181ec0d754SDag-Erling Smørgrav case PAM_ERROR_MSG: 9191ec0d754SDag-Erling Smørgrav case PAM_TEXT_INFO: 9201ec0d754SDag-Erling Smørgrav len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 9211ec0d754SDag-Erling Smørgrav buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len); 9221ec0d754SDag-Erling Smørgrav buffer_append(&loginmsg, "\n", 1 ); 9231ec0d754SDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 9241ec0d754SDag-Erling Smørgrav break; 9251ec0d754SDag-Erling Smørgrav default: 9261ec0d754SDag-Erling Smørgrav goto fail; 9271ec0d754SDag-Erling Smørgrav } 9281ec0d754SDag-Erling Smørgrav } 9291ec0d754SDag-Erling Smørgrav *resp = reply; 9301ec0d754SDag-Erling Smørgrav return (PAM_SUCCESS); 9311ec0d754SDag-Erling Smørgrav 9321ec0d754SDag-Erling Smørgrav fail: 9331ec0d754SDag-Erling Smørgrav for(i = 0; i < n; i++) { 9341ec0d754SDag-Erling Smørgrav if (reply[i].resp != NULL) 9351ec0d754SDag-Erling Smørgrav xfree(reply[i].resp); 9361ec0d754SDag-Erling Smørgrav } 9371ec0d754SDag-Erling Smørgrav xfree(reply); 9381ec0d754SDag-Erling Smørgrav return (PAM_CONV_ERR); 9391ec0d754SDag-Erling Smørgrav } 9401ec0d754SDag-Erling Smørgrav 94121e764dfSDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL }; 9421ec0d754SDag-Erling Smørgrav 9431ec0d754SDag-Erling Smørgrav void 9441ec0d754SDag-Erling Smørgrav do_pam_session(void) 9451ec0d754SDag-Erling Smørgrav { 9461ec0d754SDag-Erling Smørgrav debug3("PAM: opening session"); 9471ec0d754SDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 9481ec0d754SDag-Erling Smørgrav (const void *)&store_conv); 9491ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 9501ec0d754SDag-Erling Smørgrav fatal("PAM: failed to set PAM_CONV: %s", 9511ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 9521ec0d754SDag-Erling Smørgrav sshpam_err = pam_open_session(sshpam_handle, 0); 9531ec0d754SDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 9541ec0d754SDag-Erling Smørgrav fatal("PAM: pam_open_session(): %s", 9551ec0d754SDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 9561ec0d754SDag-Erling Smørgrav sshpam_session_open = 1; 9571ec0d754SDag-Erling Smørgrav } 9581ec0d754SDag-Erling Smørgrav 959cf2b5f3bSDag-Erling Smørgrav /* 960cf2b5f3bSDag-Erling Smørgrav * Set a PAM environment string. We need to do this so that the session 961cf2b5f3bSDag-Erling Smørgrav * modules can handle things like Kerberos/GSI credentials that appear 962cf2b5f3bSDag-Erling Smørgrav * during the ssh authentication process. 963cf2b5f3bSDag-Erling Smørgrav */ 964cf2b5f3bSDag-Erling Smørgrav int 965cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value) 96609958426SBrian Feldman { 967cf2b5f3bSDag-Erling Smørgrav int ret = 1; 968cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV 969cf2b5f3bSDag-Erling Smørgrav char *compound; 970cf2b5f3bSDag-Erling Smørgrav size_t len; 97109958426SBrian Feldman 972cf2b5f3bSDag-Erling Smørgrav len = strlen(name) + strlen(value) + 2; 973cf2b5f3bSDag-Erling Smørgrav compound = xmalloc(len); 97409958426SBrian Feldman 975cf2b5f3bSDag-Erling Smørgrav snprintf(compound, len, "%s=%s", name, value); 976cf2b5f3bSDag-Erling Smørgrav ret = pam_putenv(sshpam_handle, compound); 977cf2b5f3bSDag-Erling Smørgrav xfree(compound); 978cf2b5f3bSDag-Erling Smørgrav #endif 97909958426SBrian Feldman 980cf2b5f3bSDag-Erling Smørgrav return (ret); 981cf2b5f3bSDag-Erling Smørgrav } 98209958426SBrian Feldman 9831ec0d754SDag-Erling Smørgrav char ** 9841ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void) 985cf2b5f3bSDag-Erling Smørgrav { 9861ec0d754SDag-Erling Smørgrav return sshpam_env; 987cf2b5f3bSDag-Erling Smørgrav } 988cf2b5f3bSDag-Erling Smørgrav 989cf2b5f3bSDag-Erling Smørgrav char ** 990cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void) 991cf2b5f3bSDag-Erling Smørgrav { 992cf2b5f3bSDag-Erling Smørgrav return (pam_getenvlist(sshpam_handle)); 993cf2b5f3bSDag-Erling Smørgrav } 994cf2b5f3bSDag-Erling Smørgrav 995cf2b5f3bSDag-Erling Smørgrav void 996cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env) 997cf2b5f3bSDag-Erling Smørgrav { 998cf2b5f3bSDag-Erling Smørgrav char **envp; 999cf2b5f3bSDag-Erling Smørgrav 1000cf2b5f3bSDag-Erling Smørgrav if (env == NULL) 1001cf2b5f3bSDag-Erling Smørgrav return; 1002cf2b5f3bSDag-Erling Smørgrav 1003cf2b5f3bSDag-Erling Smørgrav for (envp = env; *envp; envp++) 1004cf2b5f3bSDag-Erling Smørgrav xfree(*envp); 1005cf2b5f3bSDag-Erling Smørgrav xfree(env); 100609958426SBrian Feldman } 100709958426SBrian Feldman 100821e764dfSDag-Erling Smørgrav /* 100921e764dfSDag-Erling Smørgrav * "Blind" conversation function for password authentication. Assumes that 101021e764dfSDag-Erling Smørgrav * echo-off prompts are for the password and stores messages for later 101121e764dfSDag-Erling Smørgrav * display. 101221e764dfSDag-Erling Smørgrav */ 101321e764dfSDag-Erling Smørgrav static int 101421e764dfSDag-Erling Smørgrav sshpam_passwd_conv(int n, struct pam_message **msg, 101521e764dfSDag-Erling Smørgrav struct pam_response **resp, void *data) 101621e764dfSDag-Erling Smørgrav { 101721e764dfSDag-Erling Smørgrav struct pam_response *reply; 101821e764dfSDag-Erling Smørgrav int i; 101921e764dfSDag-Erling Smørgrav size_t len; 102021e764dfSDag-Erling Smørgrav 102121e764dfSDag-Erling Smørgrav debug3("PAM: %s called with %d messages", __func__, n); 102221e764dfSDag-Erling Smørgrav 102321e764dfSDag-Erling Smørgrav *resp = NULL; 102421e764dfSDag-Erling Smørgrav 102521e764dfSDag-Erling Smørgrav if (n <= 0 || n > PAM_MAX_NUM_MSG) 102621e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 102721e764dfSDag-Erling Smørgrav 102821e764dfSDag-Erling Smørgrav if ((reply = malloc(n * sizeof(*reply))) == NULL) 102921e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 103021e764dfSDag-Erling Smørgrav memset(reply, 0, n * sizeof(*reply)); 103121e764dfSDag-Erling Smørgrav 103221e764dfSDag-Erling Smørgrav for (i = 0; i < n; ++i) { 103321e764dfSDag-Erling Smørgrav switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 103421e764dfSDag-Erling Smørgrav case PAM_PROMPT_ECHO_OFF: 103521e764dfSDag-Erling Smørgrav if (sshpam_password == NULL) 103621e764dfSDag-Erling Smørgrav goto fail; 103721e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup(sshpam_password)) == NULL) 103821e764dfSDag-Erling Smørgrav goto fail; 103921e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 104021e764dfSDag-Erling Smørgrav break; 104121e764dfSDag-Erling Smørgrav case PAM_ERROR_MSG: 104221e764dfSDag-Erling Smørgrav case PAM_TEXT_INFO: 104321e764dfSDag-Erling Smørgrav len = strlen(PAM_MSG_MEMBER(msg, i, msg)); 104421e764dfSDag-Erling Smørgrav if (len > 0) { 104521e764dfSDag-Erling Smørgrav buffer_append(&loginmsg, 104621e764dfSDag-Erling Smørgrav PAM_MSG_MEMBER(msg, i, msg), len); 104721e764dfSDag-Erling Smørgrav buffer_append(&loginmsg, "\n", 1); 104821e764dfSDag-Erling Smørgrav } 104921e764dfSDag-Erling Smørgrav if ((reply[i].resp = strdup("")) == NULL) 105021e764dfSDag-Erling Smørgrav goto fail; 105121e764dfSDag-Erling Smørgrav reply[i].resp_retcode = PAM_SUCCESS; 105221e764dfSDag-Erling Smørgrav break; 105321e764dfSDag-Erling Smørgrav default: 105421e764dfSDag-Erling Smørgrav goto fail; 105521e764dfSDag-Erling Smørgrav } 105621e764dfSDag-Erling Smørgrav } 105721e764dfSDag-Erling Smørgrav *resp = reply; 105821e764dfSDag-Erling Smørgrav return (PAM_SUCCESS); 105921e764dfSDag-Erling Smørgrav 106021e764dfSDag-Erling Smørgrav fail: 106121e764dfSDag-Erling Smørgrav for(i = 0; i < n; i++) { 106221e764dfSDag-Erling Smørgrav if (reply[i].resp != NULL) 106321e764dfSDag-Erling Smørgrav xfree(reply[i].resp); 106421e764dfSDag-Erling Smørgrav } 106521e764dfSDag-Erling Smørgrav xfree(reply); 106621e764dfSDag-Erling Smørgrav return (PAM_CONV_ERR); 106721e764dfSDag-Erling Smørgrav } 106821e764dfSDag-Erling Smørgrav 106921e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; 107021e764dfSDag-Erling Smørgrav 107121e764dfSDag-Erling Smørgrav /* 107221e764dfSDag-Erling Smørgrav * Attempt password authentication via PAM 107321e764dfSDag-Erling Smørgrav */ 107421e764dfSDag-Erling Smørgrav int 107521e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password) 107621e764dfSDag-Erling Smørgrav { 107721e764dfSDag-Erling Smørgrav int flags = (options.permit_empty_passwd == 0 ? 107821e764dfSDag-Erling Smørgrav PAM_DISALLOW_NULL_AUTHTOK : 0); 107921e764dfSDag-Erling Smørgrav static char badpw[] = "\b\n\r\177INCORRECT"; 108021e764dfSDag-Erling Smørgrav 108121e764dfSDag-Erling Smørgrav if (!options.use_pam || sshpam_handle == NULL) 108221e764dfSDag-Erling Smørgrav fatal("PAM: %s called when PAM disabled or failed to " 108321e764dfSDag-Erling Smørgrav "initialise.", __func__); 108421e764dfSDag-Erling Smørgrav 108521e764dfSDag-Erling Smørgrav sshpam_password = password; 108621e764dfSDag-Erling Smørgrav sshpam_authctxt = authctxt; 108721e764dfSDag-Erling Smørgrav 108821e764dfSDag-Erling Smørgrav /* 108921e764dfSDag-Erling Smørgrav * If the user logging in is invalid, or is root but is not permitted 109021e764dfSDag-Erling Smørgrav * by PermitRootLogin, use an invalid password to prevent leaking 109121e764dfSDag-Erling Smørgrav * information via timing (eg if the PAM config has a delay on fail). 109221e764dfSDag-Erling Smørgrav */ 109321e764dfSDag-Erling Smørgrav if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && 109421e764dfSDag-Erling Smørgrav options.permit_root_login != PERMIT_YES)) 109521e764dfSDag-Erling Smørgrav sshpam_password = badpw; 109621e764dfSDag-Erling Smørgrav 109721e764dfSDag-Erling Smørgrav sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 109821e764dfSDag-Erling Smørgrav (const void *)&passwd_conv); 109921e764dfSDag-Erling Smørgrav if (sshpam_err != PAM_SUCCESS) 110021e764dfSDag-Erling Smørgrav fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, 110121e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 110221e764dfSDag-Erling Smørgrav 110321e764dfSDag-Erling Smørgrav sshpam_err = pam_authenticate(sshpam_handle, flags); 110421e764dfSDag-Erling Smørgrav sshpam_password = NULL; 110521e764dfSDag-Erling Smørgrav if (sshpam_err == PAM_SUCCESS && authctxt->valid) { 110621e764dfSDag-Erling Smørgrav debug("PAM: password authentication accepted for %.100s", 110721e764dfSDag-Erling Smørgrav authctxt->user); 110821e764dfSDag-Erling Smørgrav return 1; 110921e764dfSDag-Erling Smørgrav } else { 111021e764dfSDag-Erling Smørgrav debug("PAM: password authentication failed for %.100s: %s", 111121e764dfSDag-Erling Smørgrav authctxt->valid ? authctxt->user : "an illegal user", 111221e764dfSDag-Erling Smørgrav pam_strerror(sshpam_handle, sshpam_err)); 111321e764dfSDag-Erling Smørgrav return 0; 111421e764dfSDag-Erling Smørgrav } 111521e764dfSDag-Erling Smørgrav } 111609958426SBrian Feldman #endif /* USE_PAM */ 1117