xref: /freebsd/crypto/openssh/auth-pam.c (revision b2af61ec69826890d075ceb3e20e206be20d6fea)
1cf2b5f3bSDag-Erling Smørgrav /*-
2cf2b5f3bSDag-Erling Smørgrav  * Copyright (c) 2002 Networks Associates Technology, Inc.
3cf2b5f3bSDag-Erling Smørgrav  * All rights reserved.
4cf2b5f3bSDag-Erling Smørgrav  *
5cf2b5f3bSDag-Erling Smørgrav  * This software was developed for the FreeBSD Project by ThinkSec AS and
6cf2b5f3bSDag-Erling Smørgrav  * NAI Labs, the Security Research Division of Network Associates, Inc.
7cf2b5f3bSDag-Erling Smørgrav  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8cf2b5f3bSDag-Erling Smørgrav  * DARPA CHATS research program.
909958426SBrian Feldman  *
1009958426SBrian Feldman  * Redistribution and use in source and binary forms, with or without
1109958426SBrian Feldman  * modification, are permitted provided that the following conditions
1209958426SBrian Feldman  * are met:
1309958426SBrian Feldman  * 1. Redistributions of source code must retain the above copyright
1409958426SBrian Feldman  *    notice, this list of conditions and the following disclaimer.
1509958426SBrian Feldman  * 2. Redistributions in binary form must reproduce the above copyright
1609958426SBrian Feldman  *    notice, this list of conditions and the following disclaimer in the
1709958426SBrian Feldman  *    documentation and/or other materials provided with the distribution.
1809958426SBrian Feldman  *
19cf2b5f3bSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20cf2b5f3bSDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21cf2b5f3bSDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22cf2b5f3bSDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23cf2b5f3bSDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24cf2b5f3bSDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25cf2b5f3bSDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26cf2b5f3bSDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27cf2b5f3bSDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28cf2b5f3bSDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29cf2b5f3bSDag-Erling Smørgrav  * SUCH DAMAGE.
3009958426SBrian Feldman  */
3121e764dfSDag-Erling Smørgrav /*
3221e764dfSDag-Erling Smørgrav  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
3321e764dfSDag-Erling Smørgrav  * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
3421e764dfSDag-Erling Smørgrav  *
3521e764dfSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
3621e764dfSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
3721e764dfSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
3821e764dfSDag-Erling Smørgrav  *
3921e764dfSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
4021e764dfSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
4121e764dfSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
4221e764dfSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4321e764dfSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4421e764dfSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
4521e764dfSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4621e764dfSDag-Erling Smørgrav  */
4709958426SBrian Feldman 
48acc1a9efSDag-Erling Smørgrav /* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */
49acc1a9efSDag-Erling Smørgrav 
5009958426SBrian Feldman #include "includes.h"
51333ee039SDag-Erling Smørgrav 
52333ee039SDag-Erling Smørgrav #include <sys/types.h>
53333ee039SDag-Erling Smørgrav #include <sys/stat.h>
54333ee039SDag-Erling Smørgrav #include <sys/wait.h>
55333ee039SDag-Erling Smørgrav 
56333ee039SDag-Erling Smørgrav #include <errno.h>
57333ee039SDag-Erling Smørgrav #include <signal.h>
58333ee039SDag-Erling Smørgrav #include <stdarg.h>
59333ee039SDag-Erling Smørgrav #include <string.h>
60333ee039SDag-Erling Smørgrav #include <unistd.h>
6109958426SBrian Feldman 
6209958426SBrian Feldman #ifdef USE_PAM
631ec0d754SDag-Erling Smørgrav #if defined(HAVE_SECURITY_PAM_APPL_H)
64cf2b5f3bSDag-Erling Smørgrav #include <security/pam_appl.h>
651ec0d754SDag-Erling Smørgrav #elif defined (HAVE_PAM_PAM_APPL_H)
661ec0d754SDag-Erling Smørgrav #include <pam/pam_appl.h>
671ec0d754SDag-Erling Smørgrav #endif
68cf2b5f3bSDag-Erling Smørgrav 
69d4ecd108SDag-Erling Smørgrav /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
70d4ecd108SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE
71d4ecd108SDag-Erling Smørgrav # define sshpam_const		/* Solaris, HP-UX, AIX */
72d4ecd108SDag-Erling Smørgrav #else
73d4ecd108SDag-Erling Smørgrav # define sshpam_const	const	/* LinuxPAM, OpenPAM */
74d4ecd108SDag-Erling Smørgrav #endif
75d4ecd108SDag-Erling Smørgrav 
76333ee039SDag-Erling Smørgrav /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
77333ee039SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE
78333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
79333ee039SDag-Erling Smørgrav #else
80333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
81333ee039SDag-Erling Smørgrav #endif
82333ee039SDag-Erling Smørgrav 
83333ee039SDag-Erling Smørgrav #include "xmalloc.h"
84333ee039SDag-Erling Smørgrav #include "buffer.h"
85333ee039SDag-Erling Smørgrav #include "key.h"
86333ee039SDag-Erling Smørgrav #include "hostfile.h"
87989dd127SDag-Erling Smørgrav #include "auth.h"
88989dd127SDag-Erling Smørgrav #include "auth-pam.h"
894c5de869SBrian Feldman #include "canohost.h"
90cf2b5f3bSDag-Erling Smørgrav #include "log.h"
91cf2b5f3bSDag-Erling Smørgrav #include "msg.h"
92cf2b5f3bSDag-Erling Smørgrav #include "packet.h"
9321e764dfSDag-Erling Smørgrav #include "misc.h"
94cf2b5f3bSDag-Erling Smørgrav #include "servconf.h"
95cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h"
96cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h"
97333ee039SDag-Erling Smørgrav #ifdef GSSAPI
98333ee039SDag-Erling Smørgrav #include "ssh-gss.h"
99333ee039SDag-Erling Smørgrav #endif
100333ee039SDag-Erling Smørgrav #include "monitor_wrap.h"
101*b2af61ecSKurt Lidl #include "blacklist_client.h"
10209958426SBrian Feldman 
103989dd127SDag-Erling Smørgrav extern ServerOptions options;
1041ec0d754SDag-Erling Smørgrav extern Buffer loginmsg;
1051ec0d754SDag-Erling Smørgrav extern int compat20;
1065962c0e9SDag-Erling Smørgrav extern u_int utmp_len;
1072c917d39SAlfred Perlstein 
108aa49c926SDag-Erling Smørgrav /* so we don't silently change behaviour */
109cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS
110aa49c926SDag-Erling Smørgrav # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
111aa49c926SDag-Erling Smørgrav #endif
112aa49c926SDag-Erling Smørgrav 
113aa49c926SDag-Erling Smørgrav /*
114aa49c926SDag-Erling Smørgrav  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
115aa49c926SDag-Erling Smørgrav  * and generally a bad idea.  Use at own risk and do not expect support if
116aa49c926SDag-Erling Smørgrav  * this breaks.
117aa49c926SDag-Erling Smørgrav  */
118aa49c926SDag-Erling Smørgrav #ifdef UNSUPPORTED_POSIX_THREADS_HACK
119cf2b5f3bSDag-Erling Smørgrav #include <pthread.h>
120cf2b5f3bSDag-Erling Smørgrav /*
121cf2b5f3bSDag-Erling Smørgrav  * Avoid namespace clash when *not* using pthreads for systems *with*
122cf2b5f3bSDag-Erling Smørgrav  * pthreads, which unconditionally define pthread_t via sys/types.h
123cf2b5f3bSDag-Erling Smørgrav  * (e.g. Linux)
124cf2b5f3bSDag-Erling Smørgrav  */
125cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t;
126cf2b5f3bSDag-Erling Smørgrav #else
1271ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t;
1281ec0d754SDag-Erling Smørgrav #endif
1291ec0d754SDag-Erling Smørgrav 
1301ec0d754SDag-Erling Smørgrav struct pam_ctxt {
1311ec0d754SDag-Erling Smørgrav 	sp_pthread_t	 pam_thread;
1321ec0d754SDag-Erling Smørgrav 	int		 pam_psock;
1331ec0d754SDag-Erling Smørgrav 	int		 pam_csock;
1341ec0d754SDag-Erling Smørgrav 	int		 pam_done;
1351ec0d754SDag-Erling Smørgrav };
1361ec0d754SDag-Erling Smørgrav 
1371ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *);
1381ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt;
1391ec0d754SDag-Erling Smørgrav 
140aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
141cf2b5f3bSDag-Erling Smørgrav /*
142cf2b5f3bSDag-Erling Smørgrav  * Simulate threads with processes.
143cf2b5f3bSDag-Erling Smørgrav  */
1441ec0d754SDag-Erling Smørgrav 
1451ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1;
1461ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig;
1471ec0d754SDag-Erling Smørgrav 
1481ec0d754SDag-Erling Smørgrav static void
1491ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig)
1501ec0d754SDag-Erling Smørgrav {
15121e764dfSDag-Erling Smørgrav 	signal(SIGCHLD, SIG_DFL);
1521ec0d754SDag-Erling Smørgrav 	if (cleanup_ctxt == NULL)
1531ec0d754SDag-Erling Smørgrav 		return;	/* handler called after PAM cleanup, shouldn't happen */
15421e764dfSDag-Erling Smørgrav 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
15521e764dfSDag-Erling Smørgrav 	    <= 0) {
15621e764dfSDag-Erling Smørgrav 		/* PAM thread has not exitted, privsep slave must have */
15721e764dfSDag-Erling Smørgrav 		kill(cleanup_ctxt->pam_thread, SIGTERM);
15821e764dfSDag-Erling Smørgrav 		if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
15921e764dfSDag-Erling Smørgrav 		    <= 0)
16021e764dfSDag-Erling Smørgrav 			return; /* could not wait */
16121e764dfSDag-Erling Smørgrav 	}
1621ec0d754SDag-Erling Smørgrav 	if (WIFSIGNALED(sshpam_thread_status) &&
1631ec0d754SDag-Erling Smørgrav 	    WTERMSIG(sshpam_thread_status) == SIGTERM)
1641ec0d754SDag-Erling Smørgrav 		return;	/* terminated by pthread_cancel */
1651ec0d754SDag-Erling Smørgrav 	if (!WIFEXITED(sshpam_thread_status))
166d4af9e69SDag-Erling Smørgrav 		sigdie("PAM: authentication thread exited unexpectedly");
1671ec0d754SDag-Erling Smørgrav 	if (WEXITSTATUS(sshpam_thread_status) != 0)
168d4af9e69SDag-Erling Smørgrav 		sigdie("PAM: authentication thread exited uncleanly");
1691ec0d754SDag-Erling Smørgrav }
17009958426SBrian Feldman 
171333ee039SDag-Erling Smørgrav /* ARGSUSED */
172cf2b5f3bSDag-Erling Smørgrav static void
173333ee039SDag-Erling Smørgrav pthread_exit(void *value)
17409958426SBrian Feldman {
175cf2b5f3bSDag-Erling Smørgrav 	_exit(0);
17609958426SBrian Feldman }
17709958426SBrian Feldman 
178333ee039SDag-Erling Smørgrav /* ARGSUSED */
179cf2b5f3bSDag-Erling Smørgrav static int
180333ee039SDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr,
181cf2b5f3bSDag-Erling Smørgrav     void *(*thread_start)(void *), void *arg)
182cf2b5f3bSDag-Erling Smørgrav {
183cf2b5f3bSDag-Erling Smørgrav 	pid_t pid;
184d4ecd108SDag-Erling Smørgrav 	struct pam_ctxt *ctx = arg;
185cf2b5f3bSDag-Erling Smørgrav 
1865962c0e9SDag-Erling Smørgrav 	sshpam_thread_status = -1;
187cf2b5f3bSDag-Erling Smørgrav 	switch ((pid = fork())) {
188cf2b5f3bSDag-Erling Smørgrav 	case -1:
189cf2b5f3bSDag-Erling Smørgrav 		error("fork(): %s", strerror(errno));
190cf2b5f3bSDag-Erling Smørgrav 		return (-1);
191cf2b5f3bSDag-Erling Smørgrav 	case 0:
192d4ecd108SDag-Erling Smørgrav 		close(ctx->pam_psock);
193d4ecd108SDag-Erling Smørgrav 		ctx->pam_psock = -1;
194cf2b5f3bSDag-Erling Smørgrav 		thread_start(arg);
195cf2b5f3bSDag-Erling Smørgrav 		_exit(1);
196cf2b5f3bSDag-Erling Smørgrav 	default:
197cf2b5f3bSDag-Erling Smørgrav 		*thread = pid;
198d4ecd108SDag-Erling Smørgrav 		close(ctx->pam_csock);
199d4ecd108SDag-Erling Smørgrav 		ctx->pam_csock = -1;
2001ec0d754SDag-Erling Smørgrav 		sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
201cf2b5f3bSDag-Erling Smørgrav 		return (0);
202cf2b5f3bSDag-Erling Smørgrav 	}
203cf2b5f3bSDag-Erling Smørgrav }
204cf2b5f3bSDag-Erling Smørgrav 
205cf2b5f3bSDag-Erling Smørgrav static int
206cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread)
207cf2b5f3bSDag-Erling Smørgrav {
2081ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
209cf2b5f3bSDag-Erling Smørgrav 	return (kill(thread, SIGTERM));
210cf2b5f3bSDag-Erling Smørgrav }
211cf2b5f3bSDag-Erling Smørgrav 
212333ee039SDag-Erling Smørgrav /* ARGSUSED */
213cf2b5f3bSDag-Erling Smørgrav static int
214333ee039SDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value)
215cf2b5f3bSDag-Erling Smørgrav {
216cf2b5f3bSDag-Erling Smørgrav 	int status;
217cf2b5f3bSDag-Erling Smørgrav 
2181ec0d754SDag-Erling Smørgrav 	if (sshpam_thread_status != -1)
2191ec0d754SDag-Erling Smørgrav 		return (sshpam_thread_status);
2201ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
221cf2b5f3bSDag-Erling Smørgrav 	waitpid(thread, &status, 0);
222cf2b5f3bSDag-Erling Smørgrav 	return (status);
223cf2b5f3bSDag-Erling Smørgrav }
224cf2b5f3bSDag-Erling Smørgrav #endif
225cf2b5f3bSDag-Erling Smørgrav 
226cf2b5f3bSDag-Erling Smørgrav 
227cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL;
228cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0;
229cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0;
230cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0;
231cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0;
2321ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1;
2331ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL;
2345962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL;
23521e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL;
236aa49c926SDag-Erling Smørgrav static char badpw[] = "\b\n\r\177INCORRECT";
237cf2b5f3bSDag-Erling Smørgrav 
2381ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */
2391ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST
2401ec0d754SDag-Erling Smørgrav static char **
2411ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh)
2421ec0d754SDag-Erling Smørgrav {
2431ec0d754SDag-Erling Smørgrav 	/*
2441ec0d754SDag-Erling Smørgrav 	 * XXX - If necessary, we can still support envrionment passing
2451ec0d754SDag-Erling Smørgrav 	 * for platforms without pam_getenvlist by searching for known
2461ec0d754SDag-Erling Smørgrav 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
2471ec0d754SDag-Erling Smørgrav 	 */
2481ec0d754SDag-Erling Smørgrav 	 return NULL;
2491ec0d754SDag-Erling Smørgrav }
2501ec0d754SDag-Erling Smørgrav #endif
251cf2b5f3bSDag-Erling Smørgrav 
25221e764dfSDag-Erling Smørgrav /*
25321e764dfSDag-Erling Smørgrav  * Some platforms, notably Solaris, do not enforce password complexity
25421e764dfSDag-Erling Smørgrav  * rules during pam_chauthtok() if the real uid of the calling process
25521e764dfSDag-Erling Smørgrav  * is 0, on the assumption that it's being called by "passwd" run by root.
25621e764dfSDag-Erling Smørgrav  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
25721e764dfSDag-Erling Smørgrav  * the right thing.
25821e764dfSDag-Erling Smørgrav  */
25921e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
26021e764dfSDag-Erling Smørgrav static int
26121e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
26221e764dfSDag-Erling Smørgrav {
26321e764dfSDag-Erling Smørgrav 	int result;
26421e764dfSDag-Erling Smørgrav 
26521e764dfSDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
26621e764dfSDag-Erling Smørgrav 		fatal("PAM: sshpam_authctxt not initialized");
26721e764dfSDag-Erling Smørgrav 	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
26821e764dfSDag-Erling Smørgrav 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
26921e764dfSDag-Erling Smørgrav 	result = pam_chauthtok(pamh, flags);
27021e764dfSDag-Erling Smørgrav 	if (setreuid(0, -1) == -1)
27121e764dfSDag-Erling Smørgrav 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
27221e764dfSDag-Erling Smørgrav 	return result;
27321e764dfSDag-Erling Smørgrav }
27421e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
27521e764dfSDag-Erling Smørgrav #endif
27621e764dfSDag-Erling Smørgrav 
2771ec0d754SDag-Erling Smørgrav void
27821e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd)
2791ec0d754SDag-Erling Smørgrav {
2801ec0d754SDag-Erling Smørgrav 	debug3("%s %d", __func__, reqd);
2815962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
2825962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
2835962c0e9SDag-Erling Smørgrav 	sshpam_authctxt->force_pwchange = reqd;
2841ec0d754SDag-Erling Smørgrav 	if (reqd) {
2851ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag |= 2;
2861ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag |= 2;
2871ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag |= 2;
2881ec0d754SDag-Erling Smørgrav 	} else {
2891ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag &= ~2;
2901ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag &= ~2;
2911ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag &= ~2;
2921ec0d754SDag-Erling Smørgrav 	}
2931ec0d754SDag-Erling Smørgrav }
2941ec0d754SDag-Erling Smørgrav 
2951ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */
2961ec0d754SDag-Erling Smørgrav static void
2971ec0d754SDag-Erling Smørgrav import_environments(Buffer *b)
2981ec0d754SDag-Erling Smørgrav {
2991ec0d754SDag-Erling Smørgrav 	char *env;
3001ec0d754SDag-Erling Smørgrav 	u_int i, num_env;
3011ec0d754SDag-Erling Smørgrav 	int err;
3021ec0d754SDag-Erling Smørgrav 
3031ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
3041ec0d754SDag-Erling Smørgrav 
305aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
3061ec0d754SDag-Erling Smørgrav 	/* Import variables set by do_pam_account */
3071ec0d754SDag-Erling Smørgrav 	sshpam_account_status = buffer_get_int(b);
30821e764dfSDag-Erling Smørgrav 	sshpam_password_change_required(buffer_get_int(b));
3091ec0d754SDag-Erling Smørgrav 
3101ec0d754SDag-Erling Smørgrav 	/* Import environment from subprocess */
3111ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
312333ee039SDag-Erling Smørgrav 	if (num_env > 1024)
313333ee039SDag-Erling Smørgrav 		fatal("%s: received %u environment variables, expected <= 1024",
314333ee039SDag-Erling Smørgrav 		    __func__, num_env);
315333ee039SDag-Erling Smørgrav 	sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env));
3161ec0d754SDag-Erling Smørgrav 	debug3("PAM: num env strings %d", num_env);
3171ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++)
3181ec0d754SDag-Erling Smørgrav 		sshpam_env[i] = buffer_get_string(b, NULL);
3191ec0d754SDag-Erling Smørgrav 
3201ec0d754SDag-Erling Smørgrav 	sshpam_env[num_env] = NULL;
3211ec0d754SDag-Erling Smørgrav 
3221ec0d754SDag-Erling Smørgrav 	/* Import PAM environment from subprocess */
3231ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
3241ec0d754SDag-Erling Smørgrav 	debug("PAM: num PAM env strings %d", num_env);
3251ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++) {
3261ec0d754SDag-Erling Smørgrav 		env = buffer_get_string(b, NULL);
3271ec0d754SDag-Erling Smørgrav 
3281ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
3291ec0d754SDag-Erling Smørgrav 		/* Errors are not fatal here */
3301ec0d754SDag-Erling Smørgrav 		if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
3311ec0d754SDag-Erling Smørgrav 			error("PAM: pam_putenv: %s",
3321ec0d754SDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
3331ec0d754SDag-Erling Smørgrav 		}
3341ec0d754SDag-Erling Smørgrav #endif
3351ec0d754SDag-Erling Smørgrav 	}
3365962c0e9SDag-Erling Smørgrav #endif
3371ec0d754SDag-Erling Smørgrav }
338cf2b5f3bSDag-Erling Smørgrav 
339cf2b5f3bSDag-Erling Smørgrav /*
340cf2b5f3bSDag-Erling Smørgrav  * Conversation function for authentication thread.
341cf2b5f3bSDag-Erling Smørgrav  */
342cf2b5f3bSDag-Erling Smørgrav static int
343d4ecd108SDag-Erling Smørgrav sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
344cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
345cf2b5f3bSDag-Erling Smørgrav {
346cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
347cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
348cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
349cf2b5f3bSDag-Erling Smørgrav 	int i;
350cf2b5f3bSDag-Erling Smørgrav 
3511ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
352cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
353cf2b5f3bSDag-Erling Smørgrav 
35421e764dfSDag-Erling Smørgrav 	if (data == NULL) {
35521e764dfSDag-Erling Smørgrav 		error("PAM: conversation function passed a null context");
35621e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
35721e764dfSDag-Erling Smørgrav 	}
358cf2b5f3bSDag-Erling Smørgrav 	ctxt = data;
359cf2b5f3bSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
360cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
361cf2b5f3bSDag-Erling Smørgrav 
362333ee039SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
363cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
364cf2b5f3bSDag-Erling Smørgrav 
365cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
366cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
367cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
368cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
369cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
370cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3711ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3721ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3731ec0d754SDag-Erling Smørgrav 				goto fail;
3741ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
3751ec0d754SDag-Erling Smørgrav 				goto fail;
376cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
377cf2b5f3bSDag-Erling Smørgrav 				goto fail;
378cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
37909958426SBrian Feldman 			break;
380cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
381cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
382cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3831ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3841ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3851ec0d754SDag-Erling Smørgrav 				goto fail;
3861ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
3871ec0d754SDag-Erling Smørgrav 				goto fail;
388cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
389cf2b5f3bSDag-Erling Smørgrav 				goto fail;
390cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
391cf2b5f3bSDag-Erling Smørgrav 			break;
392cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
393cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
394cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3951ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3961ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3971ec0d754SDag-Erling Smørgrav 				goto fail;
398cf2b5f3bSDag-Erling Smørgrav 			break;
399cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
400cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
401cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
4021ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
4031ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
4041ec0d754SDag-Erling Smørgrav 				goto fail;
405cf2b5f3bSDag-Erling Smørgrav 			break;
406cf2b5f3bSDag-Erling Smørgrav 		default:
407cf2b5f3bSDag-Erling Smørgrav 			goto fail;
408cf2b5f3bSDag-Erling Smørgrav 		}
409cf2b5f3bSDag-Erling Smørgrav 		buffer_clear(&buffer);
410cf2b5f3bSDag-Erling Smørgrav 	}
411cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
412cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
413cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
414cf2b5f3bSDag-Erling Smørgrav 
415cf2b5f3bSDag-Erling Smørgrav  fail:
416cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
417e4a9863fSDag-Erling Smørgrav 		free(reply[i].resp);
418cf2b5f3bSDag-Erling Smørgrav 	}
419e4a9863fSDag-Erling Smørgrav 	free(reply);
420cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
421cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
422cf2b5f3bSDag-Erling Smørgrav }
423cf2b5f3bSDag-Erling Smørgrav 
424cf2b5f3bSDag-Erling Smørgrav /*
425cf2b5f3bSDag-Erling Smørgrav  * Authentication thread.
426cf2b5f3bSDag-Erling Smørgrav  */
427cf2b5f3bSDag-Erling Smørgrav static void *
428cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp)
429cf2b5f3bSDag-Erling Smørgrav {
430cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
431cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
432cf2b5f3bSDag-Erling Smørgrav 	struct pam_conv sshpam_conv;
43321e764dfSDag-Erling Smørgrav 	int flags = (options.permit_empty_passwd == 0 ?
43421e764dfSDag-Erling Smørgrav 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
435aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
4361ec0d754SDag-Erling Smørgrav 	extern char **environ;
4371ec0d754SDag-Erling Smørgrav 	char **env_from_pam;
4381ec0d754SDag-Erling Smørgrav 	u_int i;
439cf2b5f3bSDag-Erling Smørgrav 	const char *pam_user;
440d4ecd108SDag-Erling Smørgrav 	const char **ptr_pam_user = &pam_user;
441333ee039SDag-Erling Smørgrav 	char *tz = getenv("TZ");
442cf2b5f3bSDag-Erling Smørgrav 
443f7167e0eSDag-Erling Smørgrav 	sshpam_err = pam_get_item(sshpam_handle, PAM_USER,
444d4ecd108SDag-Erling Smørgrav 	    (sshpam_const void **)ptr_pam_user);
445f7167e0eSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
446f7167e0eSDag-Erling Smørgrav 		goto auth_fail;
447333ee039SDag-Erling Smørgrav 
4481ec0d754SDag-Erling Smørgrav 	environ[0] = NULL;
449333ee039SDag-Erling Smørgrav 	if (tz != NULL)
450333ee039SDag-Erling Smørgrav 		if (setenv("TZ", tz, 1) == -1)
451333ee039SDag-Erling Smørgrav 			error("PAM: could not set TZ environment: %s",
452333ee039SDag-Erling Smørgrav 			    strerror(errno));
45321e764dfSDag-Erling Smørgrav 
45421e764dfSDag-Erling Smørgrav 	if (sshpam_authctxt != NULL) {
45521e764dfSDag-Erling Smørgrav 		setproctitle("%s [pam]",
45621e764dfSDag-Erling Smørgrav 		    sshpam_authctxt->valid ? pam_user : "unknown");
45721e764dfSDag-Erling Smørgrav 	}
458cf2b5f3bSDag-Erling Smørgrav #endif
459cf2b5f3bSDag-Erling Smørgrav 
460cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.conv = sshpam_thread_conv;
461cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.appdata_ptr = ctxt;
462cf2b5f3bSDag-Erling Smørgrav 
4635962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
4645962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
4655962c0e9SDag-Erling Smørgrav 
466cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
467cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
468cf2b5f3bSDag-Erling Smørgrav 	    (const void *)&sshpam_conv);
469cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
470cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
47121e764dfSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, flags);
472cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
473cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
4741ec0d754SDag-Erling Smørgrav 
4751ec0d754SDag-Erling Smørgrav 	if (compat20) {
476333ee039SDag-Erling Smørgrav 		if (!do_pam_account()) {
477333ee039SDag-Erling Smørgrav 			sshpam_err = PAM_ACCT_EXPIRED;
4781ec0d754SDag-Erling Smørgrav 			goto auth_fail;
479333ee039SDag-Erling Smørgrav 		}
4805962c0e9SDag-Erling Smørgrav 		if (sshpam_authctxt->force_pwchange) {
4811ec0d754SDag-Erling Smørgrav 			sshpam_err = pam_chauthtok(sshpam_handle,
4821ec0d754SDag-Erling Smørgrav 			    PAM_CHANGE_EXPIRED_AUTHTOK);
4831ec0d754SDag-Erling Smørgrav 			if (sshpam_err != PAM_SUCCESS)
4841ec0d754SDag-Erling Smørgrav 				goto auth_fail;
48521e764dfSDag-Erling Smørgrav 			sshpam_password_change_required(0);
4861ec0d754SDag-Erling Smørgrav 		}
4871ec0d754SDag-Erling Smørgrav 	}
4881ec0d754SDag-Erling Smørgrav 
489cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer, "OK");
4901ec0d754SDag-Erling Smørgrav 
491aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
4921ec0d754SDag-Erling Smørgrav 	/* Export variables set by do_pam_account */
4931ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_account_status);
4945962c0e9SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
4951ec0d754SDag-Erling Smørgrav 
4961ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set in child */
4971ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
4981ec0d754SDag-Erling Smørgrav 		; /* Count */
4991ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
5001ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
5011ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, environ[i]);
5021ec0d754SDag-Erling Smørgrav 
5031ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set by PAM in child */
5041ec0d754SDag-Erling Smørgrav 	env_from_pam = pam_getenvlist(sshpam_handle);
5051ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
5061ec0d754SDag-Erling Smørgrav 		; /* Count */
5071ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
5081ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
5091ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, env_from_pam[i]);
510aa49c926SDag-Erling Smørgrav #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
5111ec0d754SDag-Erling Smørgrav 
5121ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
513cf2b5f3bSDag-Erling Smørgrav 	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
514cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
515cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
516cf2b5f3bSDag-Erling Smørgrav 
517cf2b5f3bSDag-Erling Smørgrav  auth_fail:
518cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer,
519cf2b5f3bSDag-Erling Smørgrav 	    pam_strerror(sshpam_handle, sshpam_err));
5201ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
521333ee039SDag-Erling Smørgrav 	if (sshpam_err == PAM_ACCT_EXPIRED)
522333ee039SDag-Erling Smørgrav 		ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer);
523333ee039SDag-Erling Smørgrav 	else
524cf2b5f3bSDag-Erling Smørgrav 		ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
525cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
526cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
527cf2b5f3bSDag-Erling Smørgrav 
528cf2b5f3bSDag-Erling Smørgrav 	return (NULL); /* Avoid warning for non-pthread case */
529cf2b5f3bSDag-Erling Smørgrav }
530cf2b5f3bSDag-Erling Smørgrav 
5311ec0d754SDag-Erling Smørgrav void
5321ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void)
533cf2b5f3bSDag-Erling Smørgrav {
5341ec0d754SDag-Erling Smørgrav 	struct pam_ctxt *ctxt = cleanup_ctxt;
535cf2b5f3bSDag-Erling Smørgrav 
5361ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
5371ec0d754SDag-Erling Smørgrav 	if (ctxt != NULL && ctxt->pam_thread != 0) {
538cf2b5f3bSDag-Erling Smørgrav 		pthread_cancel(ctxt->pam_thread);
539cf2b5f3bSDag-Erling Smørgrav 		pthread_join(ctxt->pam_thread, NULL);
540cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_psock);
541cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_csock);
5421ec0d754SDag-Erling Smørgrav 		memset(ctxt, 0, sizeof(*ctxt));
5431ec0d754SDag-Erling Smørgrav 		cleanup_ctxt = NULL;
5441ec0d754SDag-Erling Smørgrav 	}
545cf2b5f3bSDag-Erling Smørgrav }
546cf2b5f3bSDag-Erling Smørgrav 
547cf2b5f3bSDag-Erling Smørgrav static int
548d4ecd108SDag-Erling Smørgrav sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
549cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
550cf2b5f3bSDag-Erling Smørgrav {
5511ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
552cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
553cf2b5f3bSDag-Erling Smørgrav }
554cf2b5f3bSDag-Erling Smørgrav 
555cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL };
556cf2b5f3bSDag-Erling Smørgrav 
557aa49c926SDag-Erling Smørgrav static int
558d4ecd108SDag-Erling Smørgrav sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
559aa49c926SDag-Erling Smørgrav     struct pam_response **resp, void *data)
560aa49c926SDag-Erling Smørgrav {
561aa49c926SDag-Erling Smørgrav 	struct pam_response *reply;
562aa49c926SDag-Erling Smørgrav 	int i;
563aa49c926SDag-Erling Smørgrav 	size_t len;
564aa49c926SDag-Erling Smørgrav 
565aa49c926SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
566aa49c926SDag-Erling Smørgrav 	*resp = NULL;
567aa49c926SDag-Erling Smørgrav 
568aa49c926SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
569aa49c926SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
570aa49c926SDag-Erling Smørgrav 
571333ee039SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
572aa49c926SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
573aa49c926SDag-Erling Smørgrav 
574aa49c926SDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
575aa49c926SDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
576aa49c926SDag-Erling Smørgrav 		case PAM_ERROR_MSG:
577aa49c926SDag-Erling Smørgrav 		case PAM_TEXT_INFO:
578aa49c926SDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
579aa49c926SDag-Erling Smørgrav 			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
580aa49c926SDag-Erling Smørgrav 			buffer_append(&loginmsg, "\n", 1 );
581aa49c926SDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
582aa49c926SDag-Erling Smørgrav 			break;
583aa49c926SDag-Erling Smørgrav 		default:
584aa49c926SDag-Erling Smørgrav 			goto fail;
585aa49c926SDag-Erling Smørgrav 		}
586aa49c926SDag-Erling Smørgrav 	}
587aa49c926SDag-Erling Smørgrav 	*resp = reply;
588aa49c926SDag-Erling Smørgrav 	return (PAM_SUCCESS);
589aa49c926SDag-Erling Smørgrav 
590aa49c926SDag-Erling Smørgrav  fail:
591aa49c926SDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
592e4a9863fSDag-Erling Smørgrav 		free(reply[i].resp);
593aa49c926SDag-Erling Smørgrav 	}
594e4a9863fSDag-Erling Smørgrav 	free(reply);
595aa49c926SDag-Erling Smørgrav 	return (PAM_CONV_ERR);
596aa49c926SDag-Erling Smørgrav }
597aa49c926SDag-Erling Smørgrav 
598aa49c926SDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL };
599aa49c926SDag-Erling Smørgrav 
6001ec0d754SDag-Erling Smørgrav void
6011ec0d754SDag-Erling Smørgrav sshpam_cleanup(void)
602cf2b5f3bSDag-Erling Smørgrav {
603d4af9e69SDag-Erling Smørgrav 	if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor()))
604cf2b5f3bSDag-Erling Smørgrav 		return;
605d4af9e69SDag-Erling Smørgrav 	debug("PAM: cleanup");
606cf2b5f3bSDag-Erling Smørgrav 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
607cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_session_open) {
608d4af9e69SDag-Erling Smørgrav 		debug("PAM: closing session");
609cf2b5f3bSDag-Erling Smørgrav 		pam_close_session(sshpam_handle, PAM_SILENT);
610cf2b5f3bSDag-Erling Smørgrav 		sshpam_session_open = 0;
611cf2b5f3bSDag-Erling Smørgrav 	}
6127aee6ffeSDag-Erling Smørgrav 	if (sshpam_cred_established) {
6137aee6ffeSDag-Erling Smørgrav 		debug("PAM: deleting credentials");
6147aee6ffeSDag-Erling Smørgrav 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
6157aee6ffeSDag-Erling Smørgrav 		sshpam_cred_established = 0;
6167aee6ffeSDag-Erling Smørgrav 	}
6171ec0d754SDag-Erling Smørgrav 	sshpam_authenticated = 0;
618cf2b5f3bSDag-Erling Smørgrav 	pam_end(sshpam_handle, sshpam_err);
619cf2b5f3bSDag-Erling Smørgrav 	sshpam_handle = NULL;
620cf2b5f3bSDag-Erling Smørgrav }
621cf2b5f3bSDag-Erling Smørgrav 
622cf2b5f3bSDag-Erling Smørgrav static int
6235962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt)
624cf2b5f3bSDag-Erling Smørgrav {
625cf2b5f3bSDag-Erling Smørgrav 	extern char *__progname;
6265962c0e9SDag-Erling Smørgrav 	const char *pam_rhost, *pam_user, *user = authctxt->user;
627d4ecd108SDag-Erling Smørgrav 	const char **ptr_pam_user = &pam_user;
628cf2b5f3bSDag-Erling Smørgrav 
629cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_handle != NULL) {
630cf2b5f3bSDag-Erling Smørgrav 		/* We already have a PAM context; check if the user matches */
631cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_get_item(sshpam_handle,
632d4ecd108SDag-Erling Smørgrav 		    PAM_USER, (sshpam_const void **)ptr_pam_user);
633cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
634cf2b5f3bSDag-Erling Smørgrav 			return (0);
635cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
636cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
637cf2b5f3bSDag-Erling Smørgrav 	}
638cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: initializing for \"%s\"", user);
639cf2b5f3bSDag-Erling Smørgrav 	sshpam_err =
640aa49c926SDag-Erling Smørgrav 	    pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
6415962c0e9SDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
6425962c0e9SDag-Erling Smørgrav 
643cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
644cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
645cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
646cf2b5f3bSDag-Erling Smørgrav 		return (-1);
647cf2b5f3bSDag-Erling Smørgrav 	}
648cf2b5f3bSDag-Erling Smørgrav 	pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
649cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
650cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
651cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
652cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
653cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
654cf2b5f3bSDag-Erling Smørgrav 		return (-1);
655cf2b5f3bSDag-Erling Smørgrav 	}
656cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE
657cf2b5f3bSDag-Erling Smørgrav 	/*
658cf2b5f3bSDag-Erling Smørgrav 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
659cf2b5f3bSDag-Erling Smørgrav 	 * sshd doesn't set the tty until too late in the auth process and
660cf2b5f3bSDag-Erling Smørgrav 	 * may not even set one (for tty-less connections)
661cf2b5f3bSDag-Erling Smørgrav 	 */
662cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_TTY to \"ssh\"");
663cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
664cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
665cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
666cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
667cf2b5f3bSDag-Erling Smørgrav 		return (-1);
668cf2b5f3bSDag-Erling Smørgrav 	}
669cf2b5f3bSDag-Erling Smørgrav #endif
670cf2b5f3bSDag-Erling Smørgrav 	return (0);
671cf2b5f3bSDag-Erling Smørgrav }
672cf2b5f3bSDag-Erling Smørgrav 
673cf2b5f3bSDag-Erling Smørgrav static void *
674cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt)
675cf2b5f3bSDag-Erling Smørgrav {
676cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
677cf2b5f3bSDag-Erling Smørgrav 	int socks[2];
678cf2b5f3bSDag-Erling Smørgrav 
6791ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
680333ee039SDag-Erling Smørgrav 	/*
681333ee039SDag-Erling Smørgrav 	 * Refuse to start if we don't have PAM enabled or do_pam_account
682333ee039SDag-Erling Smørgrav 	 * has previously failed.
683333ee039SDag-Erling Smørgrav 	 */
684333ee039SDag-Erling Smørgrav 	if (!options.use_pam || sshpam_account_status == 0)
685cf2b5f3bSDag-Erling Smørgrav 		return NULL;
686cf2b5f3bSDag-Erling Smørgrav 
687cf2b5f3bSDag-Erling Smørgrav 	/* Initialize PAM */
6885962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1) {
689cf2b5f3bSDag-Erling Smørgrav 		error("PAM: initialization failed");
690cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
691cf2b5f3bSDag-Erling Smørgrav 	}
692cf2b5f3bSDag-Erling Smørgrav 
693d4af9e69SDag-Erling Smørgrav 	ctxt = xcalloc(1, sizeof *ctxt);
6941ec0d754SDag-Erling Smørgrav 
695cf2b5f3bSDag-Erling Smørgrav 	/* Start the authentication thread */
696cf2b5f3bSDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
697cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed create sockets: %s", strerror(errno));
698e4a9863fSDag-Erling Smørgrav 		free(ctxt);
699cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
700cf2b5f3bSDag-Erling Smørgrav 	}
701cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_psock = socks[0];
702cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_csock = socks[1];
703cf2b5f3bSDag-Erling Smørgrav 	if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
704cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed to start authentication thread: %s",
705cf2b5f3bSDag-Erling Smørgrav 		    strerror(errno));
706cf2b5f3bSDag-Erling Smørgrav 		close(socks[0]);
707cf2b5f3bSDag-Erling Smørgrav 		close(socks[1]);
708e4a9863fSDag-Erling Smørgrav 		free(ctxt);
709cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
710cf2b5f3bSDag-Erling Smørgrav 	}
7111ec0d754SDag-Erling Smørgrav 	cleanup_ctxt = ctxt;
712cf2b5f3bSDag-Erling Smørgrav 	return (ctxt);
713cf2b5f3bSDag-Erling Smørgrav }
714cf2b5f3bSDag-Erling Smørgrav 
715cf2b5f3bSDag-Erling Smørgrav static int
716cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info,
717cf2b5f3bSDag-Erling Smørgrav     u_int *num, char ***prompts, u_int **echo_on)
718cf2b5f3bSDag-Erling Smørgrav {
719cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
720cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
721cf2b5f3bSDag-Erling Smørgrav 	size_t plen;
722cf2b5f3bSDag-Erling Smørgrav 	u_char type;
723cf2b5f3bSDag-Erling Smørgrav 	char *msg;
724aa49c926SDag-Erling Smørgrav 	size_t len, mlen;
725cf2b5f3bSDag-Erling Smørgrav 
7261ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
727cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
728cf2b5f3bSDag-Erling Smørgrav 	*name = xstrdup("");
729cf2b5f3bSDag-Erling Smørgrav 	*info = xstrdup("");
730cf2b5f3bSDag-Erling Smørgrav 	*prompts = xmalloc(sizeof(char *));
731cf2b5f3bSDag-Erling Smørgrav 	**prompts = NULL;
732cf2b5f3bSDag-Erling Smørgrav 	plen = 0;
733cf2b5f3bSDag-Erling Smørgrav 	*echo_on = xmalloc(sizeof(u_int));
734cf2b5f3bSDag-Erling Smørgrav 	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
735cf2b5f3bSDag-Erling Smørgrav 		type = buffer_get_char(&buffer);
736cf2b5f3bSDag-Erling Smørgrav 		msg = buffer_get_string(&buffer, NULL);
737aa49c926SDag-Erling Smørgrav 		mlen = strlen(msg);
738cf2b5f3bSDag-Erling Smørgrav 		switch (type) {
739cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
740cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
741cf2b5f3bSDag-Erling Smørgrav 			*num = 1;
742aa49c926SDag-Erling Smørgrav 			len = plen + mlen + 1;
743557f75e5SDag-Erling Smørgrav 			**prompts = xreallocarray(**prompts, 1, len);
744aa49c926SDag-Erling Smørgrav 			strlcpy(**prompts + plen, msg, len - plen);
745aa49c926SDag-Erling Smørgrav 			plen += mlen;
746cf2b5f3bSDag-Erling Smørgrav 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
747e4a9863fSDag-Erling Smørgrav 			free(msg);
748cf2b5f3bSDag-Erling Smørgrav 			return (0);
749cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
750cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
751cf2b5f3bSDag-Erling Smørgrav 			/* accumulate messages */
752aa49c926SDag-Erling Smørgrav 			len = plen + mlen + 2;
753557f75e5SDag-Erling Smørgrav 			**prompts = xreallocarray(**prompts, 1, len);
754aa49c926SDag-Erling Smørgrav 			strlcpy(**prompts + plen, msg, len - plen);
755aa49c926SDag-Erling Smørgrav 			plen += mlen;
756aa49c926SDag-Erling Smørgrav 			strlcat(**prompts + plen, "\n", len - plen);
757aa49c926SDag-Erling Smørgrav 			plen++;
758e4a9863fSDag-Erling Smørgrav 			free(msg);
759cf2b5f3bSDag-Erling Smørgrav 			break;
760333ee039SDag-Erling Smørgrav 		case PAM_ACCT_EXPIRED:
761333ee039SDag-Erling Smørgrav 			sshpam_account_status = 0;
762333ee039SDag-Erling Smørgrav 			/* FALLTHROUGH */
763cf2b5f3bSDag-Erling Smørgrav 		case PAM_AUTH_ERR:
764333ee039SDag-Erling Smørgrav 			debug3("PAM: %s", pam_strerror(sshpam_handle, type));
765b74df5b2SDag-Erling Smørgrav 			if (**prompts != NULL && strlen(**prompts) != 0) {
766b74df5b2SDag-Erling Smørgrav 				*info = **prompts;
767b74df5b2SDag-Erling Smørgrav 				**prompts = NULL;
768b74df5b2SDag-Erling Smørgrav 				*num = 0;
769b74df5b2SDag-Erling Smørgrav 				**echo_on = 0;
770b74df5b2SDag-Erling Smørgrav 				ctxt->pam_done = -1;
771e4a9863fSDag-Erling Smørgrav 				free(msg);
772b74df5b2SDag-Erling Smørgrav 				return 0;
773b74df5b2SDag-Erling Smørgrav 			}
774b74df5b2SDag-Erling Smørgrav 			/* FALLTHROUGH */
775b74df5b2SDag-Erling Smørgrav 		case PAM_SUCCESS:
776cf2b5f3bSDag-Erling Smørgrav 			if (**prompts != NULL) {
777cf2b5f3bSDag-Erling Smørgrav 				/* drain any accumulated messages */
7781ec0d754SDag-Erling Smørgrav 				debug("PAM: %s", **prompts);
7791ec0d754SDag-Erling Smørgrav 				buffer_append(&loginmsg, **prompts,
7801ec0d754SDag-Erling Smørgrav 				    strlen(**prompts));
781e4a9863fSDag-Erling Smørgrav 				free(**prompts);
782cf2b5f3bSDag-Erling Smørgrav 				**prompts = NULL;
783cf2b5f3bSDag-Erling Smørgrav 			}
784cf2b5f3bSDag-Erling Smørgrav 			if (type == PAM_SUCCESS) {
785aa49c926SDag-Erling Smørgrav 				if (!sshpam_authctxt->valid ||
786aa49c926SDag-Erling Smørgrav 				    (sshpam_authctxt->pw->pw_uid == 0 &&
787aa49c926SDag-Erling Smørgrav 				    options.permit_root_login != PERMIT_YES))
788aa49c926SDag-Erling Smørgrav 					fatal("Internal error: PAM auth "
789aa49c926SDag-Erling Smørgrav 					    "succeeded when it should have "
790aa49c926SDag-Erling Smørgrav 					    "failed");
7911ec0d754SDag-Erling Smørgrav 				import_environments(&buffer);
792cf2b5f3bSDag-Erling Smørgrav 				*num = 0;
793cf2b5f3bSDag-Erling Smørgrav 				**echo_on = 0;
794cf2b5f3bSDag-Erling Smørgrav 				ctxt->pam_done = 1;
795e4a9863fSDag-Erling Smørgrav 				free(msg);
796cf2b5f3bSDag-Erling Smørgrav 				return (0);
797cf2b5f3bSDag-Erling Smørgrav 			}
798*b2af61ecSKurt Lidl 			BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL);
7995962c0e9SDag-Erling Smørgrav 			error("PAM: %s for %s%.100s from %.100s", msg,
8005962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->valid ? "" : "illegal user ",
8015962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->user,
8025962c0e9SDag-Erling Smørgrav 			    get_remote_name_or_ip(utmp_len, options.use_dns));
8031ec0d754SDag-Erling Smørgrav 			/* FALLTHROUGH */
804cf2b5f3bSDag-Erling Smørgrav 		default:
805cf2b5f3bSDag-Erling Smørgrav 			*num = 0;
806cf2b5f3bSDag-Erling Smørgrav 			**echo_on = 0;
807e4a9863fSDag-Erling Smørgrav 			free(msg);
808cf2b5f3bSDag-Erling Smørgrav 			ctxt->pam_done = -1;
809cf2b5f3bSDag-Erling Smørgrav 			return (-1);
810cf2b5f3bSDag-Erling Smørgrav 		}
811cf2b5f3bSDag-Erling Smørgrav 	}
812cf2b5f3bSDag-Erling Smørgrav 	return (-1);
813cf2b5f3bSDag-Erling Smørgrav }
814cf2b5f3bSDag-Erling Smørgrav 
815cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */
816cf2b5f3bSDag-Erling Smørgrav static int
817cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp)
818cf2b5f3bSDag-Erling Smørgrav {
819cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
820cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
821cf2b5f3bSDag-Erling Smørgrav 
822b74df5b2SDag-Erling Smørgrav 	debug2("PAM: %s entering, %u responses", __func__, num);
823cf2b5f3bSDag-Erling Smørgrav 	switch (ctxt->pam_done) {
824cf2b5f3bSDag-Erling Smørgrav 	case 1:
825cf2b5f3bSDag-Erling Smørgrav 		sshpam_authenticated = 1;
826cf2b5f3bSDag-Erling Smørgrav 		return (0);
827cf2b5f3bSDag-Erling Smørgrav 	case 0:
828cf2b5f3bSDag-Erling Smørgrav 		break;
829cf2b5f3bSDag-Erling Smørgrav 	default:
830cf2b5f3bSDag-Erling Smørgrav 		return (-1);
831cf2b5f3bSDag-Erling Smørgrav 	}
832cf2b5f3bSDag-Erling Smørgrav 	if (num != 1) {
833cf2b5f3bSDag-Erling Smørgrav 		error("PAM: expected one response, got %u", num);
834cf2b5f3bSDag-Erling Smørgrav 		return (-1);
835cf2b5f3bSDag-Erling Smørgrav 	}
836cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
837aa49c926SDag-Erling Smørgrav 	if (sshpam_authctxt->valid &&
838aa49c926SDag-Erling Smørgrav 	    (sshpam_authctxt->pw->pw_uid != 0 ||
839aa49c926SDag-Erling Smørgrav 	    options.permit_root_login == PERMIT_YES))
840cf2b5f3bSDag-Erling Smørgrav 		buffer_put_cstring(&buffer, *resp);
841aa49c926SDag-Erling Smørgrav 	else
842aa49c926SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, badpw);
8431ec0d754SDag-Erling Smørgrav 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
8441ec0d754SDag-Erling Smørgrav 		buffer_free(&buffer);
8451ec0d754SDag-Erling Smørgrav 		return (-1);
8461ec0d754SDag-Erling Smørgrav 	}
847cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
848cf2b5f3bSDag-Erling Smørgrav 	return (1);
849cf2b5f3bSDag-Erling Smørgrav }
850cf2b5f3bSDag-Erling Smørgrav 
851cf2b5f3bSDag-Erling Smørgrav static void
852cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp)
853cf2b5f3bSDag-Erling Smørgrav {
854cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
855cf2b5f3bSDag-Erling Smørgrav 
8561ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
8571ec0d754SDag-Erling Smørgrav 	sshpam_thread_cleanup();
858e4a9863fSDag-Erling Smørgrav 	free(ctxt);
859cf2b5f3bSDag-Erling Smørgrav 	/*
860cf2b5f3bSDag-Erling Smørgrav 	 * We don't call sshpam_cleanup() here because we may need the PAM
861cf2b5f3bSDag-Erling Smørgrav 	 * handle at a later stage, e.g. when setting up a session.  It's
862cf2b5f3bSDag-Erling Smørgrav 	 * still on the cleanup list, so pam_end() *will* be called before
863cf2b5f3bSDag-Erling Smørgrav 	 * the server process terminates.
864cf2b5f3bSDag-Erling Smørgrav 	 */
865cf2b5f3bSDag-Erling Smørgrav }
866cf2b5f3bSDag-Erling Smørgrav 
867cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = {
868cf2b5f3bSDag-Erling Smørgrav 	"pam",
869cf2b5f3bSDag-Erling Smørgrav 	sshpam_init_ctx,
870cf2b5f3bSDag-Erling Smørgrav 	sshpam_query,
871cf2b5f3bSDag-Erling Smørgrav 	sshpam_respond,
872cf2b5f3bSDag-Erling Smørgrav 	sshpam_free_ctx
873cf2b5f3bSDag-Erling Smørgrav };
874cf2b5f3bSDag-Erling Smørgrav 
875cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = {
876cf2b5f3bSDag-Erling Smørgrav 	"pam",
877cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_init_ctx,
878cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_query,
879cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_respond,
880cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_free_ctx
881cf2b5f3bSDag-Erling Smørgrav };
882cf2b5f3bSDag-Erling Smørgrav 
883cf2b5f3bSDag-Erling Smørgrav /*
884cf2b5f3bSDag-Erling Smørgrav  * This replaces auth-pam.c
885cf2b5f3bSDag-Erling Smørgrav  */
886cf2b5f3bSDag-Erling Smørgrav void
8875962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt)
888cf2b5f3bSDag-Erling Smørgrav {
889cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
890cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation requested when UsePAM=no");
891cf2b5f3bSDag-Erling Smørgrav 
8925962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1)
893cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation failed");
894cf2b5f3bSDag-Erling Smørgrav }
895cf2b5f3bSDag-Erling Smørgrav 
896cf2b5f3bSDag-Erling Smørgrav void
897cf2b5f3bSDag-Erling Smørgrav finish_pam(void)
898cf2b5f3bSDag-Erling Smørgrav {
8991ec0d754SDag-Erling Smørgrav 	sshpam_cleanup();
900cf2b5f3bSDag-Erling Smørgrav }
901cf2b5f3bSDag-Erling Smørgrav 
902cf2b5f3bSDag-Erling Smørgrav u_int
903cf2b5f3bSDag-Erling Smørgrav do_pam_account(void)
904cf2b5f3bSDag-Erling Smørgrav {
905aa49c926SDag-Erling Smørgrav 	debug("%s: called", __func__);
9061ec0d754SDag-Erling Smørgrav 	if (sshpam_account_status != -1)
9071ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
9081ec0d754SDag-Erling Smørgrav 
909cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
910aa49c926SDag-Erling Smørgrav 	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
911aa49c926SDag-Erling Smørgrav 	    pam_strerror(sshpam_handle, sshpam_err));
912cf2b5f3bSDag-Erling Smørgrav 
9131ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
9141ec0d754SDag-Erling Smørgrav 		sshpam_account_status = 0;
9151ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
91609958426SBrian Feldman 	}
91709958426SBrian Feldman 
9181ec0d754SDag-Erling Smørgrav 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
91921e764dfSDag-Erling Smørgrav 		sshpam_password_change_required(1);
92009958426SBrian Feldman 
9211ec0d754SDag-Erling Smørgrav 	sshpam_account_status = 1;
9221ec0d754SDag-Erling Smørgrav 	return (sshpam_account_status);
92309958426SBrian Feldman }
92409958426SBrian Feldman 
925cf2b5f3bSDag-Erling Smørgrav void
926cf2b5f3bSDag-Erling Smørgrav do_pam_set_tty(const char *tty)
927cf2b5f3bSDag-Erling Smørgrav {
928cf2b5f3bSDag-Erling Smørgrav 	if (tty != NULL) {
929cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: setting PAM_TTY to \"%s\"", tty);
930cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
931cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err != PAM_SUCCESS)
932cf2b5f3bSDag-Erling Smørgrav 			fatal("PAM: failed to set PAM_TTY: %s",
933cf2b5f3bSDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
934cf2b5f3bSDag-Erling Smørgrav 	}
93509958426SBrian Feldman }
93609958426SBrian Feldman 
937cf2b5f3bSDag-Erling Smørgrav void
938cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init)
93909958426SBrian Feldman {
940cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
941aa49c926SDag-Erling Smørgrav 	    (const void *)&store_conv);
942cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
943cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
944cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
945cf2b5f3bSDag-Erling Smørgrav 	if (init) {
946cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: establishing credentials");
947cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
948cf2b5f3bSDag-Erling Smørgrav 	} else {
949cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: reinitializing credentials");
950cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
951cf2b5f3bSDag-Erling Smørgrav 	}
952cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS) {
953cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 1;
954989dd127SDag-Erling Smørgrav 		return;
955cf2b5f3bSDag-Erling Smørgrav 	}
956cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_authenticated)
957cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_setcred(): %s",
958cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
9592c917d39SAlfred Perlstein 	else
960cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: pam_setcred(): %s",
961cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
96209958426SBrian Feldman }
96309958426SBrian Feldman 
964cf2b5f3bSDag-Erling Smørgrav static int
965d4ecd108SDag-Erling Smørgrav sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
966cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
96709958426SBrian Feldman {
968cf2b5f3bSDag-Erling Smørgrav 	char input[PAM_MAX_MSG_SIZE];
969cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
970f388f5efSDag-Erling Smørgrav 	int i;
971f388f5efSDag-Erling Smørgrav 
9721ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
9731ec0d754SDag-Erling Smørgrav 
974cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
975cf2b5f3bSDag-Erling Smørgrav 
9761ec0d754SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
977cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
978cf2b5f3bSDag-Erling Smørgrav 
979333ee039SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
980cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
981cf2b5f3bSDag-Erling Smørgrav 
982cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
983cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
984cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
985cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp =
986cf2b5f3bSDag-Erling Smørgrav 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
987cf2b5f3bSDag-Erling Smørgrav 			    RP_ALLOW_STDIN);
988cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
989cf2b5f3bSDag-Erling Smørgrav 			break;
990cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
9911ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
992d4af9e69SDag-Erling Smørgrav 			if (fgets(input, sizeof input, stdin) == NULL)
993d4af9e69SDag-Erling Smørgrav 				input[0] = '\0';
99421e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup(input)) == NULL)
99521e764dfSDag-Erling Smørgrav 				goto fail;
996cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
997cf2b5f3bSDag-Erling Smørgrav 			break;
998cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
999cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
10001ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
1001cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
1002cf2b5f3bSDag-Erling Smørgrav 			break;
1003cf2b5f3bSDag-Erling Smørgrav 		default:
1004cf2b5f3bSDag-Erling Smørgrav 			goto fail;
1005f388f5efSDag-Erling Smørgrav 		}
1006f388f5efSDag-Erling Smørgrav 	}
1007cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
1008cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
1009cf2b5f3bSDag-Erling Smørgrav 
1010cf2b5f3bSDag-Erling Smørgrav  fail:
1011cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
1012e4a9863fSDag-Erling Smørgrav 		free(reply[i].resp);
1013cf2b5f3bSDag-Erling Smørgrav 	}
1014e4a9863fSDag-Erling Smørgrav 	free(reply);
1015cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
1016cf2b5f3bSDag-Erling Smørgrav }
1017f388f5efSDag-Erling Smørgrav 
101821e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
10191ec0d754SDag-Erling Smørgrav 
1020cf2b5f3bSDag-Erling Smørgrav /*
1021cf2b5f3bSDag-Erling Smørgrav  * XXX this should be done in the authentication phase, but ssh1 doesn't
1022cf2b5f3bSDag-Erling Smørgrav  * support that
1023cf2b5f3bSDag-Erling Smørgrav  */
1024cf2b5f3bSDag-Erling Smørgrav void
1025cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void)
102609958426SBrian Feldman {
1027cf2b5f3bSDag-Erling Smørgrav 	if (use_privsep)
1028cf2b5f3bSDag-Erling Smørgrav 		fatal("Password expired (unable to change with privsep)");
1029cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
10301ec0d754SDag-Erling Smørgrav 	    (const void *)&tty_conv);
1031cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
1032cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
1033cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
1034cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: changing password");
1035cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
1036cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
1037cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_chauthtok(): %s",
1038cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
103909958426SBrian Feldman }
104009958426SBrian Feldman 
10411ec0d754SDag-Erling Smørgrav void
10421ec0d754SDag-Erling Smørgrav do_pam_session(void)
10431ec0d754SDag-Erling Smørgrav {
10441ec0d754SDag-Erling Smørgrav 	debug3("PAM: opening session");
10451ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
10461ec0d754SDag-Erling Smørgrav 	    (const void *)&store_conv);
10471ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
10481ec0d754SDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
10491ec0d754SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
10501ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_open_session(sshpam_handle, 0);
1051aa49c926SDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS)
10521ec0d754SDag-Erling Smørgrav 		sshpam_session_open = 1;
1053aa49c926SDag-Erling Smørgrav 	else {
1054aa49c926SDag-Erling Smørgrav 		sshpam_session_open = 0;
1055aa49c926SDag-Erling Smørgrav 		disable_forwarding();
1056aa49c926SDag-Erling Smørgrav 		error("PAM: pam_open_session(): %s",
1057aa49c926SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
1058aa49c926SDag-Erling Smørgrav 	}
1059aa49c926SDag-Erling Smørgrav 
1060aa49c926SDag-Erling Smørgrav }
1061aa49c926SDag-Erling Smørgrav 
1062aa49c926SDag-Erling Smørgrav int
1063aa49c926SDag-Erling Smørgrav is_pam_session_open(void)
1064aa49c926SDag-Erling Smørgrav {
1065aa49c926SDag-Erling Smørgrav 	return sshpam_session_open;
10661ec0d754SDag-Erling Smørgrav }
10671ec0d754SDag-Erling Smørgrav 
1068cf2b5f3bSDag-Erling Smørgrav /*
1069cf2b5f3bSDag-Erling Smørgrav  * Set a PAM environment string. We need to do this so that the session
1070cf2b5f3bSDag-Erling Smørgrav  * modules can handle things like Kerberos/GSI credentials that appear
1071cf2b5f3bSDag-Erling Smørgrav  * during the ssh authentication process.
1072cf2b5f3bSDag-Erling Smørgrav  */
1073cf2b5f3bSDag-Erling Smørgrav int
1074cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value)
107509958426SBrian Feldman {
1076cf2b5f3bSDag-Erling Smørgrav 	int ret = 1;
1077cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
1078cf2b5f3bSDag-Erling Smørgrav 	char *compound;
1079cf2b5f3bSDag-Erling Smørgrav 	size_t len;
108009958426SBrian Feldman 
1081cf2b5f3bSDag-Erling Smørgrav 	len = strlen(name) + strlen(value) + 2;
1082cf2b5f3bSDag-Erling Smørgrav 	compound = xmalloc(len);
108309958426SBrian Feldman 
1084cf2b5f3bSDag-Erling Smørgrav 	snprintf(compound, len, "%s=%s", name, value);
1085cf2b5f3bSDag-Erling Smørgrav 	ret = pam_putenv(sshpam_handle, compound);
1086e4a9863fSDag-Erling Smørgrav 	free(compound);
1087cf2b5f3bSDag-Erling Smørgrav #endif
108809958426SBrian Feldman 
1089cf2b5f3bSDag-Erling Smørgrav 	return (ret);
1090cf2b5f3bSDag-Erling Smørgrav }
109109958426SBrian Feldman 
10921ec0d754SDag-Erling Smørgrav char **
10931ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void)
1094cf2b5f3bSDag-Erling Smørgrav {
10951ec0d754SDag-Erling Smørgrav 	return sshpam_env;
1096cf2b5f3bSDag-Erling Smørgrav }
1097cf2b5f3bSDag-Erling Smørgrav 
1098cf2b5f3bSDag-Erling Smørgrav char **
1099cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void)
1100cf2b5f3bSDag-Erling Smørgrav {
1101cf2b5f3bSDag-Erling Smørgrav 	return (pam_getenvlist(sshpam_handle));
1102cf2b5f3bSDag-Erling Smørgrav }
1103cf2b5f3bSDag-Erling Smørgrav 
1104cf2b5f3bSDag-Erling Smørgrav void
1105cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env)
1106cf2b5f3bSDag-Erling Smørgrav {
1107cf2b5f3bSDag-Erling Smørgrav 	char **envp;
1108cf2b5f3bSDag-Erling Smørgrav 
1109cf2b5f3bSDag-Erling Smørgrav 	if (env == NULL)
1110cf2b5f3bSDag-Erling Smørgrav 		return;
1111cf2b5f3bSDag-Erling Smørgrav 
1112cf2b5f3bSDag-Erling Smørgrav 	for (envp = env; *envp; envp++)
1113e4a9863fSDag-Erling Smørgrav 		free(*envp);
1114e4a9863fSDag-Erling Smørgrav 	free(env);
111509958426SBrian Feldman }
111609958426SBrian Feldman 
111721e764dfSDag-Erling Smørgrav /*
111821e764dfSDag-Erling Smørgrav  * "Blind" conversation function for password authentication.  Assumes that
111921e764dfSDag-Erling Smørgrav  * echo-off prompts are for the password and stores messages for later
112021e764dfSDag-Erling Smørgrav  * display.
112121e764dfSDag-Erling Smørgrav  */
112221e764dfSDag-Erling Smørgrav static int
1123d4ecd108SDag-Erling Smørgrav sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
112421e764dfSDag-Erling Smørgrav     struct pam_response **resp, void *data)
112521e764dfSDag-Erling Smørgrav {
112621e764dfSDag-Erling Smørgrav 	struct pam_response *reply;
112721e764dfSDag-Erling Smørgrav 	int i;
112821e764dfSDag-Erling Smørgrav 	size_t len;
112921e764dfSDag-Erling Smørgrav 
113021e764dfSDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
113121e764dfSDag-Erling Smørgrav 
113221e764dfSDag-Erling Smørgrav 	*resp = NULL;
113321e764dfSDag-Erling Smørgrav 
113421e764dfSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
113521e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
113621e764dfSDag-Erling Smørgrav 
1137d4af9e69SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
113821e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
113921e764dfSDag-Erling Smørgrav 
114021e764dfSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
114121e764dfSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
114221e764dfSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
114321e764dfSDag-Erling Smørgrav 			if (sshpam_password == NULL)
114421e764dfSDag-Erling Smørgrav 				goto fail;
114521e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
114621e764dfSDag-Erling Smørgrav 				goto fail;
114721e764dfSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
114821e764dfSDag-Erling Smørgrav 			break;
114921e764dfSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
115021e764dfSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
115121e764dfSDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
115221e764dfSDag-Erling Smørgrav 			if (len > 0) {
115321e764dfSDag-Erling Smørgrav 				buffer_append(&loginmsg,
115421e764dfSDag-Erling Smørgrav 				    PAM_MSG_MEMBER(msg, i, msg), len);
115521e764dfSDag-Erling Smørgrav 				buffer_append(&loginmsg, "\n", 1);
115621e764dfSDag-Erling Smørgrav 			}
115721e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup("")) == NULL)
115821e764dfSDag-Erling Smørgrav 				goto fail;
115921e764dfSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
116021e764dfSDag-Erling Smørgrav 			break;
116121e764dfSDag-Erling Smørgrav 		default:
116221e764dfSDag-Erling Smørgrav 			goto fail;
116321e764dfSDag-Erling Smørgrav 		}
116421e764dfSDag-Erling Smørgrav 	}
116521e764dfSDag-Erling Smørgrav 	*resp = reply;
116621e764dfSDag-Erling Smørgrav 	return (PAM_SUCCESS);
116721e764dfSDag-Erling Smørgrav 
116821e764dfSDag-Erling Smørgrav  fail:
116921e764dfSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
1170e4a9863fSDag-Erling Smørgrav 		free(reply[i].resp);
117121e764dfSDag-Erling Smørgrav 	}
1172e4a9863fSDag-Erling Smørgrav 	free(reply);
117321e764dfSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
117421e764dfSDag-Erling Smørgrav }
117521e764dfSDag-Erling Smørgrav 
117621e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
117721e764dfSDag-Erling Smørgrav 
117821e764dfSDag-Erling Smørgrav /*
117921e764dfSDag-Erling Smørgrav  * Attempt password authentication via PAM
118021e764dfSDag-Erling Smørgrav  */
118121e764dfSDag-Erling Smørgrav int
118221e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password)
118321e764dfSDag-Erling Smørgrav {
118421e764dfSDag-Erling Smørgrav 	int flags = (options.permit_empty_passwd == 0 ?
118521e764dfSDag-Erling Smørgrav 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
118621e764dfSDag-Erling Smørgrav 
118721e764dfSDag-Erling Smørgrav 	if (!options.use_pam || sshpam_handle == NULL)
118821e764dfSDag-Erling Smørgrav 		fatal("PAM: %s called when PAM disabled or failed to "
118921e764dfSDag-Erling Smørgrav 		    "initialise.", __func__);
119021e764dfSDag-Erling Smørgrav 
119121e764dfSDag-Erling Smørgrav 	sshpam_password = password;
119221e764dfSDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
119321e764dfSDag-Erling Smørgrav 
119421e764dfSDag-Erling Smørgrav 	/*
119521e764dfSDag-Erling Smørgrav 	 * If the user logging in is invalid, or is root but is not permitted
119621e764dfSDag-Erling Smørgrav 	 * by PermitRootLogin, use an invalid password to prevent leaking
119721e764dfSDag-Erling Smørgrav 	 * information via timing (eg if the PAM config has a delay on fail).
119821e764dfSDag-Erling Smørgrav 	 */
119921e764dfSDag-Erling Smørgrav 	if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
120021e764dfSDag-Erling Smørgrav 	    options.permit_root_login != PERMIT_YES))
120121e764dfSDag-Erling Smørgrav 		sshpam_password = badpw;
120221e764dfSDag-Erling Smørgrav 
120321e764dfSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
120421e764dfSDag-Erling Smørgrav 	    (const void *)&passwd_conv);
120521e764dfSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
120621e764dfSDag-Erling Smørgrav 		fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
120721e764dfSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
120821e764dfSDag-Erling Smørgrav 
120921e764dfSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, flags);
121021e764dfSDag-Erling Smørgrav 	sshpam_password = NULL;
121121e764dfSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
121221e764dfSDag-Erling Smørgrav 		debug("PAM: password authentication accepted for %.100s",
121321e764dfSDag-Erling Smørgrav 		    authctxt->user);
121421e764dfSDag-Erling Smørgrav 		return 1;
121521e764dfSDag-Erling Smørgrav 	} else {
121621e764dfSDag-Erling Smørgrav 		debug("PAM: password authentication failed for %.100s: %s",
121721e764dfSDag-Erling Smørgrav 		    authctxt->valid ? authctxt->user : "an illegal user",
121821e764dfSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
121921e764dfSDag-Erling Smørgrav 		return 0;
122021e764dfSDag-Erling Smørgrav 	}
122121e764dfSDag-Erling Smørgrav }
122209958426SBrian Feldman #endif /* USE_PAM */
1223