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