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