xref: /freebsd/crypto/openssh/auth-pam.c (revision d4af9e693f15f5155095f38c7650b24fe74ae351)
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 
487396b2c4SDag-Erling Smørgrav /* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
4909958426SBrian Feldman #include "includes.h"
50333ee039SDag-Erling Smørgrav 
51333ee039SDag-Erling Smørgrav #include <sys/types.h>
52333ee039SDag-Erling Smørgrav #include <sys/stat.h>
53333ee039SDag-Erling Smørgrav #include <sys/wait.h>
54333ee039SDag-Erling Smørgrav 
55333ee039SDag-Erling Smørgrav #include <errno.h>
56333ee039SDag-Erling Smørgrav #include <signal.h>
57333ee039SDag-Erling Smørgrav #include <stdarg.h>
58333ee039SDag-Erling Smørgrav #include <string.h>
59333ee039SDag-Erling Smørgrav #include <unistd.h>
6009958426SBrian Feldman 
6109958426SBrian Feldman #ifdef USE_PAM
621ec0d754SDag-Erling Smørgrav #if defined(HAVE_SECURITY_PAM_APPL_H)
63cf2b5f3bSDag-Erling Smørgrav #include <security/pam_appl.h>
641ec0d754SDag-Erling Smørgrav #elif defined (HAVE_PAM_PAM_APPL_H)
651ec0d754SDag-Erling Smørgrav #include <pam/pam_appl.h>
661ec0d754SDag-Erling Smørgrav #endif
67cf2b5f3bSDag-Erling Smørgrav 
68d4ecd108SDag-Erling Smørgrav /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
69d4ecd108SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE
70d4ecd108SDag-Erling Smørgrav # define sshpam_const		/* Solaris, HP-UX, AIX */
71d4ecd108SDag-Erling Smørgrav #else
72d4ecd108SDag-Erling Smørgrav # define sshpam_const	const	/* LinuxPAM, OpenPAM */
73d4ecd108SDag-Erling Smørgrav #endif
74d4ecd108SDag-Erling Smørgrav 
75333ee039SDag-Erling Smørgrav /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
76333ee039SDag-Erling Smørgrav #ifdef PAM_SUN_CODEBASE
77333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
78333ee039SDag-Erling Smørgrav #else
79333ee039SDag-Erling Smørgrav # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
80333ee039SDag-Erling Smørgrav #endif
81333ee039SDag-Erling Smørgrav 
82333ee039SDag-Erling Smørgrav #include "xmalloc.h"
83333ee039SDag-Erling Smørgrav #include "buffer.h"
84333ee039SDag-Erling Smørgrav #include "key.h"
85333ee039SDag-Erling Smørgrav #include "hostfile.h"
86989dd127SDag-Erling Smørgrav #include "auth.h"
87989dd127SDag-Erling Smørgrav #include "auth-pam.h"
884c5de869SBrian Feldman #include "canohost.h"
89cf2b5f3bSDag-Erling Smørgrav #include "log.h"
90cf2b5f3bSDag-Erling Smørgrav #include "msg.h"
91cf2b5f3bSDag-Erling Smørgrav #include "packet.h"
9221e764dfSDag-Erling Smørgrav #include "misc.h"
93cf2b5f3bSDag-Erling Smørgrav #include "servconf.h"
94cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h"
95cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h"
96333ee039SDag-Erling Smørgrav #ifdef GSSAPI
97333ee039SDag-Erling Smørgrav #include "ssh-gss.h"
98333ee039SDag-Erling Smørgrav #endif
99333ee039SDag-Erling Smørgrav #include "monitor_wrap.h"
10009958426SBrian Feldman 
101989dd127SDag-Erling Smørgrav extern ServerOptions options;
1021ec0d754SDag-Erling Smørgrav extern Buffer loginmsg;
1031ec0d754SDag-Erling Smørgrav extern int compat20;
1045962c0e9SDag-Erling Smørgrav extern u_int utmp_len;
1052c917d39SAlfred Perlstein 
106aa49c926SDag-Erling Smørgrav /* so we don't silently change behaviour */
107cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS
108aa49c926SDag-Erling Smørgrav # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
109aa49c926SDag-Erling Smørgrav #endif
110aa49c926SDag-Erling Smørgrav 
111aa49c926SDag-Erling Smørgrav /*
112aa49c926SDag-Erling Smørgrav  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
113aa49c926SDag-Erling Smørgrav  * and generally a bad idea.  Use at own risk and do not expect support if
114aa49c926SDag-Erling Smørgrav  * this breaks.
115aa49c926SDag-Erling Smørgrav  */
116aa49c926SDag-Erling Smørgrav #ifdef UNSUPPORTED_POSIX_THREADS_HACK
117cf2b5f3bSDag-Erling Smørgrav #include <pthread.h>
118cf2b5f3bSDag-Erling Smørgrav /*
119cf2b5f3bSDag-Erling Smørgrav  * Avoid namespace clash when *not* using pthreads for systems *with*
120cf2b5f3bSDag-Erling Smørgrav  * pthreads, which unconditionally define pthread_t via sys/types.h
121cf2b5f3bSDag-Erling Smørgrav  * (e.g. Linux)
122cf2b5f3bSDag-Erling Smørgrav  */
123cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t;
124cf2b5f3bSDag-Erling Smørgrav #else
1251ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t;
1261ec0d754SDag-Erling Smørgrav #endif
1271ec0d754SDag-Erling Smørgrav 
1281ec0d754SDag-Erling Smørgrav struct pam_ctxt {
1291ec0d754SDag-Erling Smørgrav 	sp_pthread_t	 pam_thread;
1301ec0d754SDag-Erling Smørgrav 	int		 pam_psock;
1311ec0d754SDag-Erling Smørgrav 	int		 pam_csock;
1321ec0d754SDag-Erling Smørgrav 	int		 pam_done;
1331ec0d754SDag-Erling Smørgrav };
1341ec0d754SDag-Erling Smørgrav 
1351ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *);
1361ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt;
1371ec0d754SDag-Erling Smørgrav 
138aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
139cf2b5f3bSDag-Erling Smørgrav /*
140cf2b5f3bSDag-Erling Smørgrav  * Simulate threads with processes.
141cf2b5f3bSDag-Erling Smørgrav  */
1421ec0d754SDag-Erling Smørgrav 
1431ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1;
1441ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig;
1451ec0d754SDag-Erling Smørgrav 
1461ec0d754SDag-Erling Smørgrav static void
1471ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig)
1481ec0d754SDag-Erling Smørgrav {
14921e764dfSDag-Erling Smørgrav 	signal(SIGCHLD, SIG_DFL);
1501ec0d754SDag-Erling Smørgrav 	if (cleanup_ctxt == NULL)
1511ec0d754SDag-Erling Smørgrav 		return;	/* handler called after PAM cleanup, shouldn't happen */
15221e764dfSDag-Erling Smørgrav 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
15321e764dfSDag-Erling Smørgrav 	    <= 0) {
15421e764dfSDag-Erling Smørgrav 		/* PAM thread has not exitted, privsep slave must have */
15521e764dfSDag-Erling Smørgrav 		kill(cleanup_ctxt->pam_thread, SIGTERM);
15621e764dfSDag-Erling Smørgrav 		if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
15721e764dfSDag-Erling Smørgrav 		    <= 0)
15821e764dfSDag-Erling Smørgrav 			return; /* could not wait */
15921e764dfSDag-Erling Smørgrav 	}
1601ec0d754SDag-Erling Smørgrav 	if (WIFSIGNALED(sshpam_thread_status) &&
1611ec0d754SDag-Erling Smørgrav 	    WTERMSIG(sshpam_thread_status) == SIGTERM)
1621ec0d754SDag-Erling Smørgrav 		return;	/* terminated by pthread_cancel */
1631ec0d754SDag-Erling Smørgrav 	if (!WIFEXITED(sshpam_thread_status))
164d4af9e69SDag-Erling Smørgrav 		sigdie("PAM: authentication thread exited unexpectedly");
1651ec0d754SDag-Erling Smørgrav 	if (WEXITSTATUS(sshpam_thread_status) != 0)
166d4af9e69SDag-Erling Smørgrav 		sigdie("PAM: authentication thread exited uncleanly");
1671ec0d754SDag-Erling Smørgrav }
16809958426SBrian Feldman 
169333ee039SDag-Erling Smørgrav /* ARGSUSED */
170cf2b5f3bSDag-Erling Smørgrav static void
171333ee039SDag-Erling Smørgrav pthread_exit(void *value)
17209958426SBrian Feldman {
173cf2b5f3bSDag-Erling Smørgrav 	_exit(0);
17409958426SBrian Feldman }
17509958426SBrian Feldman 
176333ee039SDag-Erling Smørgrav /* ARGSUSED */
177cf2b5f3bSDag-Erling Smørgrav static int
178333ee039SDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr,
179cf2b5f3bSDag-Erling Smørgrav     void *(*thread_start)(void *), void *arg)
180cf2b5f3bSDag-Erling Smørgrav {
181cf2b5f3bSDag-Erling Smørgrav 	pid_t pid;
182d4ecd108SDag-Erling Smørgrav 	struct pam_ctxt *ctx = arg;
183cf2b5f3bSDag-Erling Smørgrav 
1845962c0e9SDag-Erling Smørgrav 	sshpam_thread_status = -1;
185cf2b5f3bSDag-Erling Smørgrav 	switch ((pid = fork())) {
186cf2b5f3bSDag-Erling Smørgrav 	case -1:
187cf2b5f3bSDag-Erling Smørgrav 		error("fork(): %s", strerror(errno));
188cf2b5f3bSDag-Erling Smørgrav 		return (-1);
189cf2b5f3bSDag-Erling Smørgrav 	case 0:
190d4ecd108SDag-Erling Smørgrav 		close(ctx->pam_psock);
191d4ecd108SDag-Erling Smørgrav 		ctx->pam_psock = -1;
192cf2b5f3bSDag-Erling Smørgrav 		thread_start(arg);
193cf2b5f3bSDag-Erling Smørgrav 		_exit(1);
194cf2b5f3bSDag-Erling Smørgrav 	default:
195cf2b5f3bSDag-Erling Smørgrav 		*thread = pid;
196d4ecd108SDag-Erling Smørgrav 		close(ctx->pam_csock);
197d4ecd108SDag-Erling Smørgrav 		ctx->pam_csock = -1;
1981ec0d754SDag-Erling Smørgrav 		sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
199cf2b5f3bSDag-Erling Smørgrav 		return (0);
200cf2b5f3bSDag-Erling Smørgrav 	}
201cf2b5f3bSDag-Erling Smørgrav }
202cf2b5f3bSDag-Erling Smørgrav 
203cf2b5f3bSDag-Erling Smørgrav static int
204cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread)
205cf2b5f3bSDag-Erling Smørgrav {
2061ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
207cf2b5f3bSDag-Erling Smørgrav 	return (kill(thread, SIGTERM));
208cf2b5f3bSDag-Erling Smørgrav }
209cf2b5f3bSDag-Erling Smørgrav 
210333ee039SDag-Erling Smørgrav /* ARGSUSED */
211cf2b5f3bSDag-Erling Smørgrav static int
212333ee039SDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value)
213cf2b5f3bSDag-Erling Smørgrav {
214cf2b5f3bSDag-Erling Smørgrav 	int status;
215cf2b5f3bSDag-Erling Smørgrav 
2161ec0d754SDag-Erling Smørgrav 	if (sshpam_thread_status != -1)
2171ec0d754SDag-Erling Smørgrav 		return (sshpam_thread_status);
2181ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
219cf2b5f3bSDag-Erling Smørgrav 	waitpid(thread, &status, 0);
220cf2b5f3bSDag-Erling Smørgrav 	return (status);
221cf2b5f3bSDag-Erling Smørgrav }
222cf2b5f3bSDag-Erling Smørgrav #endif
223cf2b5f3bSDag-Erling Smørgrav 
224cf2b5f3bSDag-Erling Smørgrav 
225cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL;
226cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0;
227cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0;
228cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0;
229cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0;
2301ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1;
2311ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL;
2325962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL;
23321e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL;
234aa49c926SDag-Erling Smørgrav static char badpw[] = "\b\n\r\177INCORRECT";
235cf2b5f3bSDag-Erling Smørgrav 
2361ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */
2371ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST
2381ec0d754SDag-Erling Smørgrav static char **
2391ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh)
2401ec0d754SDag-Erling Smørgrav {
2411ec0d754SDag-Erling Smørgrav 	/*
2421ec0d754SDag-Erling Smørgrav 	 * XXX - If necessary, we can still support envrionment passing
2431ec0d754SDag-Erling Smørgrav 	 * for platforms without pam_getenvlist by searching for known
2441ec0d754SDag-Erling Smørgrav 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
2451ec0d754SDag-Erling Smørgrav 	 */
2461ec0d754SDag-Erling Smørgrav 	 return NULL;
2471ec0d754SDag-Erling Smørgrav }
2481ec0d754SDag-Erling Smørgrav #endif
249cf2b5f3bSDag-Erling Smørgrav 
25021e764dfSDag-Erling Smørgrav /*
25121e764dfSDag-Erling Smørgrav  * Some platforms, notably Solaris, do not enforce password complexity
25221e764dfSDag-Erling Smørgrav  * rules during pam_chauthtok() if the real uid of the calling process
25321e764dfSDag-Erling Smørgrav  * is 0, on the assumption that it's being called by "passwd" run by root.
25421e764dfSDag-Erling Smørgrav  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
25521e764dfSDag-Erling Smørgrav  * the right thing.
25621e764dfSDag-Erling Smørgrav  */
25721e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
25821e764dfSDag-Erling Smørgrav static int
25921e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
26021e764dfSDag-Erling Smørgrav {
26121e764dfSDag-Erling Smørgrav 	int result;
26221e764dfSDag-Erling Smørgrav 
26321e764dfSDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
26421e764dfSDag-Erling Smørgrav 		fatal("PAM: sshpam_authctxt not initialized");
26521e764dfSDag-Erling Smørgrav 	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
26621e764dfSDag-Erling Smørgrav 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
26721e764dfSDag-Erling Smørgrav 	result = pam_chauthtok(pamh, flags);
26821e764dfSDag-Erling Smørgrav 	if (setreuid(0, -1) == -1)
26921e764dfSDag-Erling Smørgrav 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
27021e764dfSDag-Erling Smørgrav 	return result;
27121e764dfSDag-Erling Smørgrav }
27221e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
27321e764dfSDag-Erling Smørgrav #endif
27421e764dfSDag-Erling Smørgrav 
2751ec0d754SDag-Erling Smørgrav void
27621e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd)
2771ec0d754SDag-Erling Smørgrav {
2781ec0d754SDag-Erling Smørgrav 	debug3("%s %d", __func__, reqd);
2795962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
2805962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
2815962c0e9SDag-Erling Smørgrav 	sshpam_authctxt->force_pwchange = reqd;
2821ec0d754SDag-Erling Smørgrav 	if (reqd) {
2831ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag |= 2;
2841ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag |= 2;
2851ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag |= 2;
2861ec0d754SDag-Erling Smørgrav 	} else {
2871ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag &= ~2;
2881ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag &= ~2;
2891ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag &= ~2;
2901ec0d754SDag-Erling Smørgrav 	}
2911ec0d754SDag-Erling Smørgrav }
2921ec0d754SDag-Erling Smørgrav 
2931ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */
2941ec0d754SDag-Erling Smørgrav static void
2951ec0d754SDag-Erling Smørgrav import_environments(Buffer *b)
2961ec0d754SDag-Erling Smørgrav {
2971ec0d754SDag-Erling Smørgrav 	char *env;
2981ec0d754SDag-Erling Smørgrav 	u_int i, num_env;
2991ec0d754SDag-Erling Smørgrav 	int err;
3001ec0d754SDag-Erling Smørgrav 
3011ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
3021ec0d754SDag-Erling Smørgrav 
303aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
3041ec0d754SDag-Erling Smørgrav 	/* Import variables set by do_pam_account */
3051ec0d754SDag-Erling Smørgrav 	sshpam_account_status = buffer_get_int(b);
30621e764dfSDag-Erling Smørgrav 	sshpam_password_change_required(buffer_get_int(b));
3071ec0d754SDag-Erling Smørgrav 
3081ec0d754SDag-Erling Smørgrav 	/* Import environment from subprocess */
3091ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
310333ee039SDag-Erling Smørgrav 	if (num_env > 1024)
311333ee039SDag-Erling Smørgrav 		fatal("%s: received %u environment variables, expected <= 1024",
312333ee039SDag-Erling Smørgrav 		    __func__, num_env);
313333ee039SDag-Erling Smørgrav 	sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env));
3141ec0d754SDag-Erling Smørgrav 	debug3("PAM: num env strings %d", num_env);
3151ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++)
3161ec0d754SDag-Erling Smørgrav 		sshpam_env[i] = buffer_get_string(b, NULL);
3171ec0d754SDag-Erling Smørgrav 
3181ec0d754SDag-Erling Smørgrav 	sshpam_env[num_env] = NULL;
3191ec0d754SDag-Erling Smørgrav 
3201ec0d754SDag-Erling Smørgrav 	/* Import PAM environment from subprocess */
3211ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
3221ec0d754SDag-Erling Smørgrav 	debug("PAM: num PAM env strings %d", num_env);
3231ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++) {
3241ec0d754SDag-Erling Smørgrav 		env = buffer_get_string(b, NULL);
3251ec0d754SDag-Erling Smørgrav 
3261ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
3271ec0d754SDag-Erling Smørgrav 		/* Errors are not fatal here */
3281ec0d754SDag-Erling Smørgrav 		if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
3291ec0d754SDag-Erling Smørgrav 			error("PAM: pam_putenv: %s",
3301ec0d754SDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
3311ec0d754SDag-Erling Smørgrav 		}
3321ec0d754SDag-Erling Smørgrav #endif
3331ec0d754SDag-Erling Smørgrav 	}
3345962c0e9SDag-Erling Smørgrav #endif
3351ec0d754SDag-Erling Smørgrav }
336cf2b5f3bSDag-Erling Smørgrav 
337cf2b5f3bSDag-Erling Smørgrav /*
338cf2b5f3bSDag-Erling Smørgrav  * Conversation function for authentication thread.
339cf2b5f3bSDag-Erling Smørgrav  */
340cf2b5f3bSDag-Erling Smørgrav static int
341d4ecd108SDag-Erling Smørgrav sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
342cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
343cf2b5f3bSDag-Erling Smørgrav {
344cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
345cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
346cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
347cf2b5f3bSDag-Erling Smørgrav 	int i;
348cf2b5f3bSDag-Erling Smørgrav 
3491ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
350cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
351cf2b5f3bSDag-Erling Smørgrav 
35221e764dfSDag-Erling Smørgrav 	if (data == NULL) {
35321e764dfSDag-Erling Smørgrav 		error("PAM: conversation function passed a null context");
35421e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
35521e764dfSDag-Erling Smørgrav 	}
356cf2b5f3bSDag-Erling Smørgrav 	ctxt = data;
357cf2b5f3bSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
358cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
359cf2b5f3bSDag-Erling Smørgrav 
360333ee039SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
361cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
362cf2b5f3bSDag-Erling Smørgrav 
363cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
364cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
365cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
366cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
367cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
368cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3691ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3701ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3711ec0d754SDag-Erling Smørgrav 				goto fail;
3721ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
3731ec0d754SDag-Erling Smørgrav 				goto fail;
374cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
375cf2b5f3bSDag-Erling Smørgrav 				goto fail;
376cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
37709958426SBrian Feldman 			break;
378cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
379cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
380cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3811ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3821ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3831ec0d754SDag-Erling Smørgrav 				goto fail;
3841ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
3851ec0d754SDag-Erling Smørgrav 				goto fail;
386cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
387cf2b5f3bSDag-Erling Smørgrav 				goto fail;
388cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
389cf2b5f3bSDag-Erling Smørgrav 			break;
390cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
391cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
392cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3931ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3941ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3951ec0d754SDag-Erling Smørgrav 				goto fail;
396cf2b5f3bSDag-Erling Smørgrav 			break;
397cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
398cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
399cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
4001ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
4011ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
4021ec0d754SDag-Erling Smørgrav 				goto fail;
403cf2b5f3bSDag-Erling Smørgrav 			break;
404cf2b5f3bSDag-Erling Smørgrav 		default:
405cf2b5f3bSDag-Erling Smørgrav 			goto fail;
406cf2b5f3bSDag-Erling Smørgrav 		}
407cf2b5f3bSDag-Erling Smørgrav 		buffer_clear(&buffer);
408cf2b5f3bSDag-Erling Smørgrav 	}
409cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
410cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
411cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
412cf2b5f3bSDag-Erling Smørgrav 
413cf2b5f3bSDag-Erling Smørgrav  fail:
414cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
415cf2b5f3bSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
416cf2b5f3bSDag-Erling Smørgrav 			xfree(reply[i].resp);
417cf2b5f3bSDag-Erling Smørgrav 	}
418cf2b5f3bSDag-Erling Smørgrav 	xfree(reply);
419cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
420cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
421cf2b5f3bSDag-Erling Smørgrav }
422cf2b5f3bSDag-Erling Smørgrav 
423cf2b5f3bSDag-Erling Smørgrav /*
424cf2b5f3bSDag-Erling Smørgrav  * Authentication thread.
425cf2b5f3bSDag-Erling Smørgrav  */
426cf2b5f3bSDag-Erling Smørgrav static void *
427cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp)
428cf2b5f3bSDag-Erling Smørgrav {
429cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
430cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
431cf2b5f3bSDag-Erling Smørgrav 	struct pam_conv sshpam_conv;
43221e764dfSDag-Erling Smørgrav 	int flags = (options.permit_empty_passwd == 0 ?
43321e764dfSDag-Erling Smørgrav 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
434aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
4351ec0d754SDag-Erling Smørgrav 	extern char **environ;
4361ec0d754SDag-Erling Smørgrav 	char **env_from_pam;
4371ec0d754SDag-Erling Smørgrav 	u_int i;
438cf2b5f3bSDag-Erling Smørgrav 	const char *pam_user;
439d4ecd108SDag-Erling Smørgrav 	const char **ptr_pam_user = &pam_user;
440333ee039SDag-Erling Smørgrav 	char *tz = getenv("TZ");
441cf2b5f3bSDag-Erling Smørgrav 
442d4ecd108SDag-Erling Smørgrav 	pam_get_item(sshpam_handle, PAM_USER,
443d4ecd108SDag-Erling Smørgrav 	    (sshpam_const void **)ptr_pam_user);
444333ee039SDag-Erling Smørgrav 
4451ec0d754SDag-Erling Smørgrav 	environ[0] = NULL;
446333ee039SDag-Erling Smørgrav 	if (tz != NULL)
447333ee039SDag-Erling Smørgrav 		if (setenv("TZ", tz, 1) == -1)
448333ee039SDag-Erling Smørgrav 			error("PAM: could not set TZ environment: %s",
449333ee039SDag-Erling Smørgrav 			    strerror(errno));
45021e764dfSDag-Erling Smørgrav 
45121e764dfSDag-Erling Smørgrav 	if (sshpam_authctxt != NULL) {
45221e764dfSDag-Erling Smørgrav 		setproctitle("%s [pam]",
45321e764dfSDag-Erling Smørgrav 		    sshpam_authctxt->valid ? pam_user : "unknown");
45421e764dfSDag-Erling Smørgrav 	}
455cf2b5f3bSDag-Erling Smørgrav #endif
456cf2b5f3bSDag-Erling Smørgrav 
457cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.conv = sshpam_thread_conv;
458cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.appdata_ptr = ctxt;
459cf2b5f3bSDag-Erling Smørgrav 
4605962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
4615962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
4625962c0e9SDag-Erling Smørgrav 
463cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
464cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
465cf2b5f3bSDag-Erling Smørgrav 	    (const void *)&sshpam_conv);
466cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
467cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
46821e764dfSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, flags);
469cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
470cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
4711ec0d754SDag-Erling Smørgrav 
4721ec0d754SDag-Erling Smørgrav 	if (compat20) {
473333ee039SDag-Erling Smørgrav 		if (!do_pam_account()) {
474333ee039SDag-Erling Smørgrav 			sshpam_err = PAM_ACCT_EXPIRED;
4751ec0d754SDag-Erling Smørgrav 			goto auth_fail;
476333ee039SDag-Erling Smørgrav 		}
4775962c0e9SDag-Erling Smørgrav 		if (sshpam_authctxt->force_pwchange) {
4781ec0d754SDag-Erling Smørgrav 			sshpam_err = pam_chauthtok(sshpam_handle,
4791ec0d754SDag-Erling Smørgrav 			    PAM_CHANGE_EXPIRED_AUTHTOK);
4801ec0d754SDag-Erling Smørgrav 			if (sshpam_err != PAM_SUCCESS)
4811ec0d754SDag-Erling Smørgrav 				goto auth_fail;
48221e764dfSDag-Erling Smørgrav 			sshpam_password_change_required(0);
4831ec0d754SDag-Erling Smørgrav 		}
4841ec0d754SDag-Erling Smørgrav 	}
4851ec0d754SDag-Erling Smørgrav 
486cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer, "OK");
4871ec0d754SDag-Erling Smørgrav 
488aa49c926SDag-Erling Smørgrav #ifndef UNSUPPORTED_POSIX_THREADS_HACK
4891ec0d754SDag-Erling Smørgrav 	/* Export variables set by do_pam_account */
4901ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_account_status);
4915962c0e9SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
4921ec0d754SDag-Erling Smørgrav 
4931ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set in child */
4941ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
4951ec0d754SDag-Erling Smørgrav 		; /* Count */
4961ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
4971ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
4981ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, environ[i]);
4991ec0d754SDag-Erling Smørgrav 
5001ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set by PAM in child */
5011ec0d754SDag-Erling Smørgrav 	env_from_pam = pam_getenvlist(sshpam_handle);
5021ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
5031ec0d754SDag-Erling Smørgrav 		; /* Count */
5041ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
5051ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
5061ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, env_from_pam[i]);
507aa49c926SDag-Erling Smørgrav #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
5081ec0d754SDag-Erling Smørgrav 
5091ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
510cf2b5f3bSDag-Erling Smørgrav 	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
511cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
512cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
513cf2b5f3bSDag-Erling Smørgrav 
514cf2b5f3bSDag-Erling Smørgrav  auth_fail:
515cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer,
516cf2b5f3bSDag-Erling Smørgrav 	    pam_strerror(sshpam_handle, sshpam_err));
5171ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
518333ee039SDag-Erling Smørgrav 	if (sshpam_err == PAM_ACCT_EXPIRED)
519333ee039SDag-Erling Smørgrav 		ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer);
520333ee039SDag-Erling Smørgrav 	else
521cf2b5f3bSDag-Erling Smørgrav 		ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
522cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
523cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
524cf2b5f3bSDag-Erling Smørgrav 
525cf2b5f3bSDag-Erling Smørgrav 	return (NULL); /* Avoid warning for non-pthread case */
526cf2b5f3bSDag-Erling Smørgrav }
527cf2b5f3bSDag-Erling Smørgrav 
5281ec0d754SDag-Erling Smørgrav void
5291ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void)
530cf2b5f3bSDag-Erling Smørgrav {
5311ec0d754SDag-Erling Smørgrav 	struct pam_ctxt *ctxt = cleanup_ctxt;
532cf2b5f3bSDag-Erling Smørgrav 
5331ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
5341ec0d754SDag-Erling Smørgrav 	if (ctxt != NULL && ctxt->pam_thread != 0) {
535cf2b5f3bSDag-Erling Smørgrav 		pthread_cancel(ctxt->pam_thread);
536cf2b5f3bSDag-Erling Smørgrav 		pthread_join(ctxt->pam_thread, NULL);
537cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_psock);
538cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_csock);
5391ec0d754SDag-Erling Smørgrav 		memset(ctxt, 0, sizeof(*ctxt));
5401ec0d754SDag-Erling Smørgrav 		cleanup_ctxt = NULL;
5411ec0d754SDag-Erling Smørgrav 	}
542cf2b5f3bSDag-Erling Smørgrav }
543cf2b5f3bSDag-Erling Smørgrav 
544cf2b5f3bSDag-Erling Smørgrav static int
545d4ecd108SDag-Erling Smørgrav sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
546cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
547cf2b5f3bSDag-Erling Smørgrav {
5481ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
549cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
550cf2b5f3bSDag-Erling Smørgrav }
551cf2b5f3bSDag-Erling Smørgrav 
552cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL };
553cf2b5f3bSDag-Erling Smørgrav 
554aa49c926SDag-Erling Smørgrav static int
555d4ecd108SDag-Erling Smørgrav sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
556aa49c926SDag-Erling Smørgrav     struct pam_response **resp, void *data)
557aa49c926SDag-Erling Smørgrav {
558aa49c926SDag-Erling Smørgrav 	struct pam_response *reply;
559aa49c926SDag-Erling Smørgrav 	int i;
560aa49c926SDag-Erling Smørgrav 	size_t len;
561aa49c926SDag-Erling Smørgrav 
562aa49c926SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
563aa49c926SDag-Erling Smørgrav 	*resp = NULL;
564aa49c926SDag-Erling Smørgrav 
565aa49c926SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
566aa49c926SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
567aa49c926SDag-Erling Smørgrav 
568333ee039SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
569aa49c926SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
570aa49c926SDag-Erling Smørgrav 
571aa49c926SDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
572aa49c926SDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
573aa49c926SDag-Erling Smørgrav 		case PAM_ERROR_MSG:
574aa49c926SDag-Erling Smørgrav 		case PAM_TEXT_INFO:
575aa49c926SDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
576aa49c926SDag-Erling Smørgrav 			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
577aa49c926SDag-Erling Smørgrav 			buffer_append(&loginmsg, "\n", 1 );
578aa49c926SDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
579aa49c926SDag-Erling Smørgrav 			break;
580aa49c926SDag-Erling Smørgrav 		default:
581aa49c926SDag-Erling Smørgrav 			goto fail;
582aa49c926SDag-Erling Smørgrav 		}
583aa49c926SDag-Erling Smørgrav 	}
584aa49c926SDag-Erling Smørgrav 	*resp = reply;
585aa49c926SDag-Erling Smørgrav 	return (PAM_SUCCESS);
586aa49c926SDag-Erling Smørgrav 
587aa49c926SDag-Erling Smørgrav  fail:
588aa49c926SDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
589aa49c926SDag-Erling Smørgrav 		if (reply[i].resp != NULL)
590aa49c926SDag-Erling Smørgrav 			xfree(reply[i].resp);
591aa49c926SDag-Erling Smørgrav 	}
592aa49c926SDag-Erling Smørgrav 	xfree(reply);
593aa49c926SDag-Erling Smørgrav 	return (PAM_CONV_ERR);
594aa49c926SDag-Erling Smørgrav }
595aa49c926SDag-Erling Smørgrav 
596aa49c926SDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL };
597aa49c926SDag-Erling Smørgrav 
5981ec0d754SDag-Erling Smørgrav void
5991ec0d754SDag-Erling Smørgrav sshpam_cleanup(void)
600cf2b5f3bSDag-Erling Smørgrav {
601d4af9e69SDag-Erling Smørgrav 	if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor()))
602cf2b5f3bSDag-Erling Smørgrav 		return;
603d4af9e69SDag-Erling Smørgrav 	debug("PAM: cleanup");
604cf2b5f3bSDag-Erling Smørgrav 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
605cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_cred_established) {
606d4af9e69SDag-Erling Smørgrav 		debug("PAM: deleting credentials");
607cf2b5f3bSDag-Erling Smørgrav 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
608cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 0;
609cf2b5f3bSDag-Erling Smørgrav 	}
610cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_session_open) {
611d4af9e69SDag-Erling Smørgrav 		debug("PAM: closing session");
612cf2b5f3bSDag-Erling Smørgrav 		pam_close_session(sshpam_handle, PAM_SILENT);
613cf2b5f3bSDag-Erling Smørgrav 		sshpam_session_open = 0;
614cf2b5f3bSDag-Erling Smørgrav 	}
6151ec0d754SDag-Erling Smørgrav 	sshpam_authenticated = 0;
616cf2b5f3bSDag-Erling Smørgrav 	pam_end(sshpam_handle, sshpam_err);
617cf2b5f3bSDag-Erling Smørgrav 	sshpam_handle = NULL;
618cf2b5f3bSDag-Erling Smørgrav }
619cf2b5f3bSDag-Erling Smørgrav 
620cf2b5f3bSDag-Erling Smørgrav static int
6215962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt)
622cf2b5f3bSDag-Erling Smørgrav {
623cf2b5f3bSDag-Erling Smørgrav 	extern char *__progname;
6245962c0e9SDag-Erling Smørgrav 	const char *pam_rhost, *pam_user, *user = authctxt->user;
625d4ecd108SDag-Erling Smørgrav 	const char **ptr_pam_user = &pam_user;
626cf2b5f3bSDag-Erling Smørgrav 
627cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_handle != NULL) {
628cf2b5f3bSDag-Erling Smørgrav 		/* We already have a PAM context; check if the user matches */
629cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_get_item(sshpam_handle,
630d4ecd108SDag-Erling Smørgrav 		    PAM_USER, (sshpam_const void **)ptr_pam_user);
631cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
632cf2b5f3bSDag-Erling Smørgrav 			return (0);
633cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
634cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
635cf2b5f3bSDag-Erling Smørgrav 	}
636cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: initializing for \"%s\"", user);
637cf2b5f3bSDag-Erling Smørgrav 	sshpam_err =
638aa49c926SDag-Erling Smørgrav 	    pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
6395962c0e9SDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
6405962c0e9SDag-Erling Smørgrav 
641cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
642cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
643cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
644cf2b5f3bSDag-Erling Smørgrav 		return (-1);
645cf2b5f3bSDag-Erling Smørgrav 	}
646cf2b5f3bSDag-Erling Smørgrav 	pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
647cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
648cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
649cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
650cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
651cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
652cf2b5f3bSDag-Erling Smørgrav 		return (-1);
653cf2b5f3bSDag-Erling Smørgrav 	}
654cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE
655cf2b5f3bSDag-Erling Smørgrav 	/*
656cf2b5f3bSDag-Erling Smørgrav 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
657cf2b5f3bSDag-Erling Smørgrav 	 * sshd doesn't set the tty until too late in the auth process and
658cf2b5f3bSDag-Erling Smørgrav 	 * may not even set one (for tty-less connections)
659cf2b5f3bSDag-Erling Smørgrav 	 */
660cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_TTY to \"ssh\"");
661cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
662cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
663cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
664cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
665cf2b5f3bSDag-Erling Smørgrav 		return (-1);
666cf2b5f3bSDag-Erling Smørgrav 	}
667cf2b5f3bSDag-Erling Smørgrav #endif
668cf2b5f3bSDag-Erling Smørgrav 	return (0);
669cf2b5f3bSDag-Erling Smørgrav }
670cf2b5f3bSDag-Erling Smørgrav 
671cf2b5f3bSDag-Erling Smørgrav static void *
672cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt)
673cf2b5f3bSDag-Erling Smørgrav {
674cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
675cf2b5f3bSDag-Erling Smørgrav 	int socks[2];
676cf2b5f3bSDag-Erling Smørgrav 
6771ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
678333ee039SDag-Erling Smørgrav 	/*
679333ee039SDag-Erling Smørgrav 	 * Refuse to start if we don't have PAM enabled or do_pam_account
680333ee039SDag-Erling Smørgrav 	 * has previously failed.
681333ee039SDag-Erling Smørgrav 	 */
682333ee039SDag-Erling Smørgrav 	if (!options.use_pam || sshpam_account_status == 0)
683cf2b5f3bSDag-Erling Smørgrav 		return NULL;
684cf2b5f3bSDag-Erling Smørgrav 
685cf2b5f3bSDag-Erling Smørgrav 	/* Initialize PAM */
6865962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1) {
687cf2b5f3bSDag-Erling Smørgrav 		error("PAM: initialization failed");
688cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
689cf2b5f3bSDag-Erling Smørgrav 	}
690cf2b5f3bSDag-Erling Smørgrav 
691d4af9e69SDag-Erling Smørgrav 	ctxt = xcalloc(1, sizeof *ctxt);
6921ec0d754SDag-Erling Smørgrav 
693cf2b5f3bSDag-Erling Smørgrav 	/* Start the authentication thread */
694cf2b5f3bSDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
695cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed create sockets: %s", strerror(errno));
696cf2b5f3bSDag-Erling Smørgrav 		xfree(ctxt);
697cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
698cf2b5f3bSDag-Erling Smørgrav 	}
699cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_psock = socks[0];
700cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_csock = socks[1];
701cf2b5f3bSDag-Erling Smørgrav 	if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
702cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed to start authentication thread: %s",
703cf2b5f3bSDag-Erling Smørgrav 		    strerror(errno));
704cf2b5f3bSDag-Erling Smørgrav 		close(socks[0]);
705cf2b5f3bSDag-Erling Smørgrav 		close(socks[1]);
706cf2b5f3bSDag-Erling Smørgrav 		xfree(ctxt);
707cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
708cf2b5f3bSDag-Erling Smørgrav 	}
7091ec0d754SDag-Erling Smørgrav 	cleanup_ctxt = ctxt;
710cf2b5f3bSDag-Erling Smørgrav 	return (ctxt);
711cf2b5f3bSDag-Erling Smørgrav }
712cf2b5f3bSDag-Erling Smørgrav 
713cf2b5f3bSDag-Erling Smørgrav static int
714cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info,
715cf2b5f3bSDag-Erling Smørgrav     u_int *num, char ***prompts, u_int **echo_on)
716cf2b5f3bSDag-Erling Smørgrav {
717cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
718cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
719cf2b5f3bSDag-Erling Smørgrav 	size_t plen;
720cf2b5f3bSDag-Erling Smørgrav 	u_char type;
721cf2b5f3bSDag-Erling Smørgrav 	char *msg;
722aa49c926SDag-Erling Smørgrav 	size_t len, mlen;
723cf2b5f3bSDag-Erling Smørgrav 
7241ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
725cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
726cf2b5f3bSDag-Erling Smørgrav 	*name = xstrdup("");
727cf2b5f3bSDag-Erling Smørgrav 	*info = xstrdup("");
728cf2b5f3bSDag-Erling Smørgrav 	*prompts = xmalloc(sizeof(char *));
729cf2b5f3bSDag-Erling Smørgrav 	**prompts = NULL;
730cf2b5f3bSDag-Erling Smørgrav 	plen = 0;
731cf2b5f3bSDag-Erling Smørgrav 	*echo_on = xmalloc(sizeof(u_int));
732cf2b5f3bSDag-Erling Smørgrav 	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
733cf2b5f3bSDag-Erling Smørgrav 		type = buffer_get_char(&buffer);
734cf2b5f3bSDag-Erling Smørgrav 		msg = buffer_get_string(&buffer, NULL);
735aa49c926SDag-Erling Smørgrav 		mlen = strlen(msg);
736cf2b5f3bSDag-Erling Smørgrav 		switch (type) {
737cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
738cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
739cf2b5f3bSDag-Erling Smørgrav 			*num = 1;
740aa49c926SDag-Erling Smørgrav 			len = plen + mlen + 1;
741333ee039SDag-Erling Smørgrav 			**prompts = xrealloc(**prompts, 1, len);
742aa49c926SDag-Erling Smørgrav 			strlcpy(**prompts + plen, msg, len - plen);
743aa49c926SDag-Erling Smørgrav 			plen += mlen;
744cf2b5f3bSDag-Erling Smørgrav 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
745cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
746cf2b5f3bSDag-Erling Smørgrav 			return (0);
747cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
748cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
749cf2b5f3bSDag-Erling Smørgrav 			/* accumulate messages */
750aa49c926SDag-Erling Smørgrav 			len = plen + mlen + 2;
751333ee039SDag-Erling Smørgrav 			**prompts = xrealloc(**prompts, 1, len);
752aa49c926SDag-Erling Smørgrav 			strlcpy(**prompts + plen, msg, len - plen);
753aa49c926SDag-Erling Smørgrav 			plen += mlen;
754aa49c926SDag-Erling Smørgrav 			strlcat(**prompts + plen, "\n", len - plen);
755aa49c926SDag-Erling Smørgrav 			plen++;
756cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
757cf2b5f3bSDag-Erling Smørgrav 			break;
758333ee039SDag-Erling Smørgrav 		case PAM_ACCT_EXPIRED:
759333ee039SDag-Erling Smørgrav 			sshpam_account_status = 0;
760333ee039SDag-Erling Smørgrav 			/* FALLTHROUGH */
761cf2b5f3bSDag-Erling Smørgrav 		case PAM_AUTH_ERR:
762333ee039SDag-Erling Smørgrav 			debug3("PAM: %s", pam_strerror(sshpam_handle, type));
763b74df5b2SDag-Erling Smørgrav 			if (**prompts != NULL && strlen(**prompts) != 0) {
764b74df5b2SDag-Erling Smørgrav 				*info = **prompts;
765b74df5b2SDag-Erling Smørgrav 				**prompts = NULL;
766b74df5b2SDag-Erling Smørgrav 				*num = 0;
767b74df5b2SDag-Erling Smørgrav 				**echo_on = 0;
768b74df5b2SDag-Erling Smørgrav 				ctxt->pam_done = -1;
769333ee039SDag-Erling Smørgrav 				xfree(msg);
770b74df5b2SDag-Erling Smørgrav 				return 0;
771b74df5b2SDag-Erling Smørgrav 			}
772b74df5b2SDag-Erling Smørgrav 			/* FALLTHROUGH */
773b74df5b2SDag-Erling Smørgrav 		case PAM_SUCCESS:
774cf2b5f3bSDag-Erling Smørgrav 			if (**prompts != NULL) {
775cf2b5f3bSDag-Erling Smørgrav 				/* drain any accumulated messages */
7761ec0d754SDag-Erling Smørgrav 				debug("PAM: %s", **prompts);
7771ec0d754SDag-Erling Smørgrav 				buffer_append(&loginmsg, **prompts,
7781ec0d754SDag-Erling Smørgrav 				    strlen(**prompts));
779cf2b5f3bSDag-Erling Smørgrav 				xfree(**prompts);
780cf2b5f3bSDag-Erling Smørgrav 				**prompts = NULL;
781cf2b5f3bSDag-Erling Smørgrav 			}
782cf2b5f3bSDag-Erling Smørgrav 			if (type == PAM_SUCCESS) {
783aa49c926SDag-Erling Smørgrav 				if (!sshpam_authctxt->valid ||
784aa49c926SDag-Erling Smørgrav 				    (sshpam_authctxt->pw->pw_uid == 0 &&
785aa49c926SDag-Erling Smørgrav 				    options.permit_root_login != PERMIT_YES))
786aa49c926SDag-Erling Smørgrav 					fatal("Internal error: PAM auth "
787aa49c926SDag-Erling Smørgrav 					    "succeeded when it should have "
788aa49c926SDag-Erling Smørgrav 					    "failed");
7891ec0d754SDag-Erling Smørgrav 				import_environments(&buffer);
790cf2b5f3bSDag-Erling Smørgrav 				*num = 0;
791cf2b5f3bSDag-Erling Smørgrav 				**echo_on = 0;
792cf2b5f3bSDag-Erling Smørgrav 				ctxt->pam_done = 1;
793cf2b5f3bSDag-Erling Smørgrav 				xfree(msg);
794cf2b5f3bSDag-Erling Smørgrav 				return (0);
795cf2b5f3bSDag-Erling Smørgrav 			}
7965962c0e9SDag-Erling Smørgrav 			error("PAM: %s for %s%.100s from %.100s", msg,
7975962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->valid ? "" : "illegal user ",
7985962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->user,
7995962c0e9SDag-Erling Smørgrav 			    get_remote_name_or_ip(utmp_len, options.use_dns));
8001ec0d754SDag-Erling Smørgrav 			/* FALLTHROUGH */
801cf2b5f3bSDag-Erling Smørgrav 		default:
802cf2b5f3bSDag-Erling Smørgrav 			*num = 0;
803cf2b5f3bSDag-Erling Smørgrav 			**echo_on = 0;
804cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
805cf2b5f3bSDag-Erling Smørgrav 			ctxt->pam_done = -1;
806cf2b5f3bSDag-Erling Smørgrav 			return (-1);
807cf2b5f3bSDag-Erling Smørgrav 		}
808cf2b5f3bSDag-Erling Smørgrav 	}
809cf2b5f3bSDag-Erling Smørgrav 	return (-1);
810cf2b5f3bSDag-Erling Smørgrav }
811cf2b5f3bSDag-Erling Smørgrav 
812cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */
813cf2b5f3bSDag-Erling Smørgrav static int
814cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp)
815cf2b5f3bSDag-Erling Smørgrav {
816cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
817cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
818cf2b5f3bSDag-Erling Smørgrav 
819b74df5b2SDag-Erling Smørgrav 	debug2("PAM: %s entering, %u responses", __func__, num);
820cf2b5f3bSDag-Erling Smørgrav 	switch (ctxt->pam_done) {
821cf2b5f3bSDag-Erling Smørgrav 	case 1:
822cf2b5f3bSDag-Erling Smørgrav 		sshpam_authenticated = 1;
823cf2b5f3bSDag-Erling Smørgrav 		return (0);
824cf2b5f3bSDag-Erling Smørgrav 	case 0:
825cf2b5f3bSDag-Erling Smørgrav 		break;
826cf2b5f3bSDag-Erling Smørgrav 	default:
827cf2b5f3bSDag-Erling Smørgrav 		return (-1);
828cf2b5f3bSDag-Erling Smørgrav 	}
829cf2b5f3bSDag-Erling Smørgrav 	if (num != 1) {
830cf2b5f3bSDag-Erling Smørgrav 		error("PAM: expected one response, got %u", num);
831cf2b5f3bSDag-Erling Smørgrav 		return (-1);
832cf2b5f3bSDag-Erling Smørgrav 	}
833cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
834aa49c926SDag-Erling Smørgrav 	if (sshpam_authctxt->valid &&
835aa49c926SDag-Erling Smørgrav 	    (sshpam_authctxt->pw->pw_uid != 0 ||
836aa49c926SDag-Erling Smørgrav 	    options.permit_root_login == PERMIT_YES))
837cf2b5f3bSDag-Erling Smørgrav 		buffer_put_cstring(&buffer, *resp);
838aa49c926SDag-Erling Smørgrav 	else
839aa49c926SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, badpw);
8401ec0d754SDag-Erling Smørgrav 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
8411ec0d754SDag-Erling Smørgrav 		buffer_free(&buffer);
8421ec0d754SDag-Erling Smørgrav 		return (-1);
8431ec0d754SDag-Erling Smørgrav 	}
844cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
845cf2b5f3bSDag-Erling Smørgrav 	return (1);
846cf2b5f3bSDag-Erling Smørgrav }
847cf2b5f3bSDag-Erling Smørgrav 
848cf2b5f3bSDag-Erling Smørgrav static void
849cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp)
850cf2b5f3bSDag-Erling Smørgrav {
851cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
852cf2b5f3bSDag-Erling Smørgrav 
8531ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
8541ec0d754SDag-Erling Smørgrav 	sshpam_thread_cleanup();
855cf2b5f3bSDag-Erling Smørgrav 	xfree(ctxt);
856cf2b5f3bSDag-Erling Smørgrav 	/*
857cf2b5f3bSDag-Erling Smørgrav 	 * We don't call sshpam_cleanup() here because we may need the PAM
858cf2b5f3bSDag-Erling Smørgrav 	 * handle at a later stage, e.g. when setting up a session.  It's
859cf2b5f3bSDag-Erling Smørgrav 	 * still on the cleanup list, so pam_end() *will* be called before
860cf2b5f3bSDag-Erling Smørgrav 	 * the server process terminates.
861cf2b5f3bSDag-Erling Smørgrav 	 */
862cf2b5f3bSDag-Erling Smørgrav }
863cf2b5f3bSDag-Erling Smørgrav 
864cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = {
865cf2b5f3bSDag-Erling Smørgrav 	"pam",
866cf2b5f3bSDag-Erling Smørgrav 	sshpam_init_ctx,
867cf2b5f3bSDag-Erling Smørgrav 	sshpam_query,
868cf2b5f3bSDag-Erling Smørgrav 	sshpam_respond,
869cf2b5f3bSDag-Erling Smørgrav 	sshpam_free_ctx
870cf2b5f3bSDag-Erling Smørgrav };
871cf2b5f3bSDag-Erling Smørgrav 
872cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = {
873cf2b5f3bSDag-Erling Smørgrav 	"pam",
874cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_init_ctx,
875cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_query,
876cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_respond,
877cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_free_ctx
878cf2b5f3bSDag-Erling Smørgrav };
879cf2b5f3bSDag-Erling Smørgrav 
880cf2b5f3bSDag-Erling Smørgrav /*
881cf2b5f3bSDag-Erling Smørgrav  * This replaces auth-pam.c
882cf2b5f3bSDag-Erling Smørgrav  */
883cf2b5f3bSDag-Erling Smørgrav void
8845962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt)
885cf2b5f3bSDag-Erling Smørgrav {
886cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
887cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation requested when UsePAM=no");
888cf2b5f3bSDag-Erling Smørgrav 
8895962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1)
890cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation failed");
891cf2b5f3bSDag-Erling Smørgrav }
892cf2b5f3bSDag-Erling Smørgrav 
893cf2b5f3bSDag-Erling Smørgrav void
894cf2b5f3bSDag-Erling Smørgrav finish_pam(void)
895cf2b5f3bSDag-Erling Smørgrav {
8961ec0d754SDag-Erling Smørgrav 	sshpam_cleanup();
897cf2b5f3bSDag-Erling Smørgrav }
898cf2b5f3bSDag-Erling Smørgrav 
899cf2b5f3bSDag-Erling Smørgrav u_int
900cf2b5f3bSDag-Erling Smørgrav do_pam_account(void)
901cf2b5f3bSDag-Erling Smørgrav {
902aa49c926SDag-Erling Smørgrav 	debug("%s: called", __func__);
9031ec0d754SDag-Erling Smørgrav 	if (sshpam_account_status != -1)
9041ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
9051ec0d754SDag-Erling Smørgrav 
906cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
907aa49c926SDag-Erling Smørgrav 	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
908aa49c926SDag-Erling Smørgrav 	    pam_strerror(sshpam_handle, sshpam_err));
909cf2b5f3bSDag-Erling Smørgrav 
9101ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
9111ec0d754SDag-Erling Smørgrav 		sshpam_account_status = 0;
9121ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
91309958426SBrian Feldman 	}
91409958426SBrian Feldman 
9151ec0d754SDag-Erling Smørgrav 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
91621e764dfSDag-Erling Smørgrav 		sshpam_password_change_required(1);
91709958426SBrian Feldman 
9181ec0d754SDag-Erling Smørgrav 	sshpam_account_status = 1;
9191ec0d754SDag-Erling Smørgrav 	return (sshpam_account_status);
92009958426SBrian Feldman }
92109958426SBrian Feldman 
922cf2b5f3bSDag-Erling Smørgrav void
923cf2b5f3bSDag-Erling Smørgrav do_pam_set_tty(const char *tty)
924cf2b5f3bSDag-Erling Smørgrav {
925cf2b5f3bSDag-Erling Smørgrav 	if (tty != NULL) {
926cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: setting PAM_TTY to \"%s\"", tty);
927cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
928cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err != PAM_SUCCESS)
929cf2b5f3bSDag-Erling Smørgrav 			fatal("PAM: failed to set PAM_TTY: %s",
930cf2b5f3bSDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
931cf2b5f3bSDag-Erling Smørgrav 	}
93209958426SBrian Feldman }
93309958426SBrian Feldman 
934cf2b5f3bSDag-Erling Smørgrav void
935cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init)
93609958426SBrian Feldman {
937cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
938aa49c926SDag-Erling Smørgrav 	    (const void *)&store_conv);
939cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
940cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
941cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
942cf2b5f3bSDag-Erling Smørgrav 	if (init) {
943cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: establishing credentials");
944cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
945cf2b5f3bSDag-Erling Smørgrav 	} else {
946cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: reinitializing credentials");
947cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
948cf2b5f3bSDag-Erling Smørgrav 	}
949cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS) {
950cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 1;
951989dd127SDag-Erling Smørgrav 		return;
952cf2b5f3bSDag-Erling Smørgrav 	}
953cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_authenticated)
954cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_setcred(): %s",
955cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
9562c917d39SAlfred Perlstein 	else
957cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: pam_setcred(): %s",
958cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
95909958426SBrian Feldman }
96009958426SBrian Feldman 
961cf2b5f3bSDag-Erling Smørgrav static int
962d4ecd108SDag-Erling Smørgrav sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
963cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
96409958426SBrian Feldman {
965cf2b5f3bSDag-Erling Smørgrav 	char input[PAM_MAX_MSG_SIZE];
966cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
967f388f5efSDag-Erling Smørgrav 	int i;
968f388f5efSDag-Erling Smørgrav 
9691ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
9701ec0d754SDag-Erling Smørgrav 
971cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
972cf2b5f3bSDag-Erling Smørgrav 
9731ec0d754SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
974cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
975cf2b5f3bSDag-Erling Smørgrav 
976333ee039SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
977cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
978cf2b5f3bSDag-Erling Smørgrav 
979cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
980cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
981cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
982cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp =
983cf2b5f3bSDag-Erling Smørgrav 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
984cf2b5f3bSDag-Erling Smørgrav 			    RP_ALLOW_STDIN);
985cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
986cf2b5f3bSDag-Erling Smørgrav 			break;
987cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
9881ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
989d4af9e69SDag-Erling Smørgrav 			if (fgets(input, sizeof input, stdin) == NULL)
990d4af9e69SDag-Erling Smørgrav 				input[0] = '\0';
99121e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup(input)) == NULL)
99221e764dfSDag-Erling Smørgrav 				goto fail;
993cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
994cf2b5f3bSDag-Erling Smørgrav 			break;
995cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
996cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
9971ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
998cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
999cf2b5f3bSDag-Erling Smørgrav 			break;
1000cf2b5f3bSDag-Erling Smørgrav 		default:
1001cf2b5f3bSDag-Erling Smørgrav 			goto fail;
1002f388f5efSDag-Erling Smørgrav 		}
1003f388f5efSDag-Erling Smørgrav 	}
1004cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
1005cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
1006cf2b5f3bSDag-Erling Smørgrav 
1007cf2b5f3bSDag-Erling Smørgrav  fail:
1008cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
1009cf2b5f3bSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
1010cf2b5f3bSDag-Erling Smørgrav 			xfree(reply[i].resp);
1011cf2b5f3bSDag-Erling Smørgrav 	}
1012cf2b5f3bSDag-Erling Smørgrav 	xfree(reply);
1013cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
1014cf2b5f3bSDag-Erling Smørgrav }
1015f388f5efSDag-Erling Smørgrav 
101621e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
10171ec0d754SDag-Erling Smørgrav 
1018cf2b5f3bSDag-Erling Smørgrav /*
1019cf2b5f3bSDag-Erling Smørgrav  * XXX this should be done in the authentication phase, but ssh1 doesn't
1020cf2b5f3bSDag-Erling Smørgrav  * support that
1021cf2b5f3bSDag-Erling Smørgrav  */
1022cf2b5f3bSDag-Erling Smørgrav void
1023cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void)
102409958426SBrian Feldman {
1025cf2b5f3bSDag-Erling Smørgrav 	if (use_privsep)
1026cf2b5f3bSDag-Erling Smørgrav 		fatal("Password expired (unable to change with privsep)");
1027cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
10281ec0d754SDag-Erling Smørgrav 	    (const void *)&tty_conv);
1029cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
1030cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
1031cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
1032cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: changing password");
1033cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
1034cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
1035cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_chauthtok(): %s",
1036cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
103709958426SBrian Feldman }
103809958426SBrian Feldman 
10391ec0d754SDag-Erling Smørgrav void
10401ec0d754SDag-Erling Smørgrav do_pam_session(void)
10411ec0d754SDag-Erling Smørgrav {
10421ec0d754SDag-Erling Smørgrav 	debug3("PAM: opening session");
10431ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
10441ec0d754SDag-Erling Smørgrav 	    (const void *)&store_conv);
10451ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
10461ec0d754SDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
10471ec0d754SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
10481ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_open_session(sshpam_handle, 0);
1049aa49c926SDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS)
10501ec0d754SDag-Erling Smørgrav 		sshpam_session_open = 1;
1051aa49c926SDag-Erling Smørgrav 	else {
1052aa49c926SDag-Erling Smørgrav 		sshpam_session_open = 0;
1053aa49c926SDag-Erling Smørgrav 		disable_forwarding();
1054aa49c926SDag-Erling Smørgrav 		error("PAM: pam_open_session(): %s",
1055aa49c926SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
1056aa49c926SDag-Erling Smørgrav 	}
1057aa49c926SDag-Erling Smørgrav 
1058aa49c926SDag-Erling Smørgrav }
1059aa49c926SDag-Erling Smørgrav 
1060aa49c926SDag-Erling Smørgrav int
1061aa49c926SDag-Erling Smørgrav is_pam_session_open(void)
1062aa49c926SDag-Erling Smørgrav {
1063aa49c926SDag-Erling Smørgrav 	return sshpam_session_open;
10641ec0d754SDag-Erling Smørgrav }
10651ec0d754SDag-Erling Smørgrav 
1066cf2b5f3bSDag-Erling Smørgrav /*
1067cf2b5f3bSDag-Erling Smørgrav  * Set a PAM environment string. We need to do this so that the session
1068cf2b5f3bSDag-Erling Smørgrav  * modules can handle things like Kerberos/GSI credentials that appear
1069cf2b5f3bSDag-Erling Smørgrav  * during the ssh authentication process.
1070cf2b5f3bSDag-Erling Smørgrav  */
1071cf2b5f3bSDag-Erling Smørgrav int
1072cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value)
107309958426SBrian Feldman {
1074cf2b5f3bSDag-Erling Smørgrav 	int ret = 1;
1075cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
1076cf2b5f3bSDag-Erling Smørgrav 	char *compound;
1077cf2b5f3bSDag-Erling Smørgrav 	size_t len;
107809958426SBrian Feldman 
1079cf2b5f3bSDag-Erling Smørgrav 	len = strlen(name) + strlen(value) + 2;
1080cf2b5f3bSDag-Erling Smørgrav 	compound = xmalloc(len);
108109958426SBrian Feldman 
1082cf2b5f3bSDag-Erling Smørgrav 	snprintf(compound, len, "%s=%s", name, value);
1083cf2b5f3bSDag-Erling Smørgrav 	ret = pam_putenv(sshpam_handle, compound);
1084cf2b5f3bSDag-Erling Smørgrav 	xfree(compound);
1085cf2b5f3bSDag-Erling Smørgrav #endif
108609958426SBrian Feldman 
1087cf2b5f3bSDag-Erling Smørgrav 	return (ret);
1088cf2b5f3bSDag-Erling Smørgrav }
108909958426SBrian Feldman 
10901ec0d754SDag-Erling Smørgrav char **
10911ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void)
1092cf2b5f3bSDag-Erling Smørgrav {
10931ec0d754SDag-Erling Smørgrav 	return sshpam_env;
1094cf2b5f3bSDag-Erling Smørgrav }
1095cf2b5f3bSDag-Erling Smørgrav 
1096cf2b5f3bSDag-Erling Smørgrav char **
1097cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void)
1098cf2b5f3bSDag-Erling Smørgrav {
1099cf2b5f3bSDag-Erling Smørgrav 	return (pam_getenvlist(sshpam_handle));
1100cf2b5f3bSDag-Erling Smørgrav }
1101cf2b5f3bSDag-Erling Smørgrav 
1102cf2b5f3bSDag-Erling Smørgrav void
1103cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env)
1104cf2b5f3bSDag-Erling Smørgrav {
1105cf2b5f3bSDag-Erling Smørgrav 	char **envp;
1106cf2b5f3bSDag-Erling Smørgrav 
1107cf2b5f3bSDag-Erling Smørgrav 	if (env == NULL)
1108cf2b5f3bSDag-Erling Smørgrav 		return;
1109cf2b5f3bSDag-Erling Smørgrav 
1110cf2b5f3bSDag-Erling Smørgrav 	for (envp = env; *envp; envp++)
1111cf2b5f3bSDag-Erling Smørgrav 		xfree(*envp);
1112cf2b5f3bSDag-Erling Smørgrav 	xfree(env);
111309958426SBrian Feldman }
111409958426SBrian Feldman 
111521e764dfSDag-Erling Smørgrav /*
111621e764dfSDag-Erling Smørgrav  * "Blind" conversation function for password authentication.  Assumes that
111721e764dfSDag-Erling Smørgrav  * echo-off prompts are for the password and stores messages for later
111821e764dfSDag-Erling Smørgrav  * display.
111921e764dfSDag-Erling Smørgrav  */
112021e764dfSDag-Erling Smørgrav static int
1121d4ecd108SDag-Erling Smørgrav sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
112221e764dfSDag-Erling Smørgrav     struct pam_response **resp, void *data)
112321e764dfSDag-Erling Smørgrav {
112421e764dfSDag-Erling Smørgrav 	struct pam_response *reply;
112521e764dfSDag-Erling Smørgrav 	int i;
112621e764dfSDag-Erling Smørgrav 	size_t len;
112721e764dfSDag-Erling Smørgrav 
112821e764dfSDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
112921e764dfSDag-Erling Smørgrav 
113021e764dfSDag-Erling Smørgrav 	*resp = NULL;
113121e764dfSDag-Erling Smørgrav 
113221e764dfSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
113321e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
113421e764dfSDag-Erling Smørgrav 
1135d4af9e69SDag-Erling Smørgrav 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
113621e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
113721e764dfSDag-Erling Smørgrav 
113821e764dfSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
113921e764dfSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
114021e764dfSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
114121e764dfSDag-Erling Smørgrav 			if (sshpam_password == NULL)
114221e764dfSDag-Erling Smørgrav 				goto fail;
114321e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
114421e764dfSDag-Erling Smørgrav 				goto fail;
114521e764dfSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
114621e764dfSDag-Erling Smørgrav 			break;
114721e764dfSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
114821e764dfSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
114921e764dfSDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
115021e764dfSDag-Erling Smørgrav 			if (len > 0) {
115121e764dfSDag-Erling Smørgrav 				buffer_append(&loginmsg,
115221e764dfSDag-Erling Smørgrav 				    PAM_MSG_MEMBER(msg, i, msg), len);
115321e764dfSDag-Erling Smørgrav 				buffer_append(&loginmsg, "\n", 1);
115421e764dfSDag-Erling Smørgrav 			}
115521e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup("")) == NULL)
115621e764dfSDag-Erling Smørgrav 				goto fail;
115721e764dfSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
115821e764dfSDag-Erling Smørgrav 			break;
115921e764dfSDag-Erling Smørgrav 		default:
116021e764dfSDag-Erling Smørgrav 			goto fail;
116121e764dfSDag-Erling Smørgrav 		}
116221e764dfSDag-Erling Smørgrav 	}
116321e764dfSDag-Erling Smørgrav 	*resp = reply;
116421e764dfSDag-Erling Smørgrav 	return (PAM_SUCCESS);
116521e764dfSDag-Erling Smørgrav 
116621e764dfSDag-Erling Smørgrav  fail:
116721e764dfSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
116821e764dfSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
116921e764dfSDag-Erling Smørgrav 			xfree(reply[i].resp);
117021e764dfSDag-Erling Smørgrav 	}
117121e764dfSDag-Erling Smørgrav 	xfree(reply);
117221e764dfSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
117321e764dfSDag-Erling Smørgrav }
117421e764dfSDag-Erling Smørgrav 
117521e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
117621e764dfSDag-Erling Smørgrav 
117721e764dfSDag-Erling Smørgrav /*
117821e764dfSDag-Erling Smørgrav  * Attempt password authentication via PAM
117921e764dfSDag-Erling Smørgrav  */
118021e764dfSDag-Erling Smørgrav int
118121e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password)
118221e764dfSDag-Erling Smørgrav {
118321e764dfSDag-Erling Smørgrav 	int flags = (options.permit_empty_passwd == 0 ?
118421e764dfSDag-Erling Smørgrav 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
118521e764dfSDag-Erling Smørgrav 
118621e764dfSDag-Erling Smørgrav 	if (!options.use_pam || sshpam_handle == NULL)
118721e764dfSDag-Erling Smørgrav 		fatal("PAM: %s called when PAM disabled or failed to "
118821e764dfSDag-Erling Smørgrav 		    "initialise.", __func__);
118921e764dfSDag-Erling Smørgrav 
119021e764dfSDag-Erling Smørgrav 	sshpam_password = password;
119121e764dfSDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
119221e764dfSDag-Erling Smørgrav 
119321e764dfSDag-Erling Smørgrav 	/*
119421e764dfSDag-Erling Smørgrav 	 * If the user logging in is invalid, or is root but is not permitted
119521e764dfSDag-Erling Smørgrav 	 * by PermitRootLogin, use an invalid password to prevent leaking
119621e764dfSDag-Erling Smørgrav 	 * information via timing (eg if the PAM config has a delay on fail).
119721e764dfSDag-Erling Smørgrav 	 */
119821e764dfSDag-Erling Smørgrav 	if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
119921e764dfSDag-Erling Smørgrav 	    options.permit_root_login != PERMIT_YES))
120021e764dfSDag-Erling Smørgrav 		sshpam_password = badpw;
120121e764dfSDag-Erling Smørgrav 
120221e764dfSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
120321e764dfSDag-Erling Smørgrav 	    (const void *)&passwd_conv);
120421e764dfSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
120521e764dfSDag-Erling Smørgrav 		fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
120621e764dfSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
120721e764dfSDag-Erling Smørgrav 
120821e764dfSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, flags);
120921e764dfSDag-Erling Smørgrav 	sshpam_password = NULL;
121021e764dfSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
121121e764dfSDag-Erling Smørgrav 		debug("PAM: password authentication accepted for %.100s",
121221e764dfSDag-Erling Smørgrav 		    authctxt->user);
121321e764dfSDag-Erling Smørgrav 		return 1;
121421e764dfSDag-Erling Smørgrav 	} else {
121521e764dfSDag-Erling Smørgrav 		debug("PAM: password authentication failed for %.100s: %s",
121621e764dfSDag-Erling Smørgrav 		    authctxt->valid ? authctxt->user : "an illegal user",
121721e764dfSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
121821e764dfSDag-Erling Smørgrav 		return 0;
121921e764dfSDag-Erling Smørgrav 	}
122021e764dfSDag-Erling Smørgrav }
122109958426SBrian Feldman #endif /* USE_PAM */
1222