xref: /dragonfly/crypto/openssh/auth-pam.c (revision 50a69bb5)
118de8d7fSPeter Avalos /*-
218de8d7fSPeter Avalos  * Copyright (c) 2002 Networks Associates Technology, Inc.
318de8d7fSPeter Avalos  * All rights reserved.
418de8d7fSPeter Avalos  *
518de8d7fSPeter Avalos  * This software was developed for the FreeBSD Project by ThinkSec AS and
618de8d7fSPeter Avalos  * NAI Labs, the Security Research Division of Network Associates, Inc.
718de8d7fSPeter Avalos  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
818de8d7fSPeter Avalos  * DARPA CHATS research program.
918de8d7fSPeter Avalos  *
1018de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
1118de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
1218de8d7fSPeter Avalos  * are met:
1318de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
1418de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1518de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1618de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1718de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
1818de8d7fSPeter Avalos  *
1918de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2018de8d7fSPeter Avalos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2118de8d7fSPeter Avalos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2218de8d7fSPeter Avalos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2318de8d7fSPeter Avalos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2418de8d7fSPeter Avalos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2518de8d7fSPeter Avalos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2618de8d7fSPeter Avalos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2718de8d7fSPeter Avalos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2818de8d7fSPeter Avalos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2918de8d7fSPeter Avalos  * SUCH DAMAGE.
3018de8d7fSPeter Avalos  */
3118de8d7fSPeter Avalos /*
3218de8d7fSPeter Avalos  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
3318de8d7fSPeter Avalos  * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
3418de8d7fSPeter Avalos  *
3518de8d7fSPeter Avalos  * Permission to use, copy, modify, and distribute this software for any
3618de8d7fSPeter Avalos  * purpose with or without fee is hereby granted, provided that the above
3718de8d7fSPeter Avalos  * copyright notice and this permission notice appear in all copies.
3818de8d7fSPeter Avalos  *
3918de8d7fSPeter Avalos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
4018de8d7fSPeter Avalos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
4118de8d7fSPeter Avalos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
4218de8d7fSPeter Avalos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4318de8d7fSPeter Avalos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4418de8d7fSPeter Avalos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
4518de8d7fSPeter Avalos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4618de8d7fSPeter Avalos  */
4718de8d7fSPeter Avalos 
48e9778795SPeter Avalos /* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */
49e9778795SPeter Avalos 
5018de8d7fSPeter Avalos #include "includes.h"
5118de8d7fSPeter Avalos 
5218de8d7fSPeter Avalos #include <sys/types.h>
5318de8d7fSPeter Avalos #include <sys/stat.h>
5418de8d7fSPeter Avalos #include <sys/wait.h>
5518de8d7fSPeter Avalos 
5618de8d7fSPeter Avalos #include <errno.h>
5718de8d7fSPeter Avalos #include <signal.h>
5818de8d7fSPeter Avalos #include <stdarg.h>
590cbfa66cSDaniel Fojt #include <stdlib.h>
6018de8d7fSPeter Avalos #include <string.h>
6118de8d7fSPeter Avalos #include <unistd.h>
6218de8d7fSPeter Avalos 
6318de8d7fSPeter Avalos #ifdef USE_PAM
6418de8d7fSPeter Avalos #if defined(HAVE_SECURITY_PAM_APPL_H)
6518de8d7fSPeter Avalos #include <security/pam_appl.h>
6618de8d7fSPeter Avalos #elif defined (HAVE_PAM_PAM_APPL_H)
6718de8d7fSPeter Avalos #include <pam/pam_appl.h>
6818de8d7fSPeter Avalos #endif
6918de8d7fSPeter Avalos 
70ce74bacaSMatthew Dillon #if !defined(SSHD_PAM_SERVICE)
71ce74bacaSMatthew Dillon extern char *__progname;
72ce74bacaSMatthew Dillon # define SSHD_PAM_SERVICE		__progname
73ce74bacaSMatthew Dillon #endif
74ce74bacaSMatthew Dillon 
7518de8d7fSPeter Avalos /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
7618de8d7fSPeter Avalos #ifdef PAM_SUN_CODEBASE
77e9778795SPeter Avalos # define sshpam_const		/* Solaris, HP-UX, SunOS */
7818de8d7fSPeter Avalos #else
79e9778795SPeter Avalos # define sshpam_const	const	/* LinuxPAM, OpenPAM, AIX */
8018de8d7fSPeter Avalos #endif
8118de8d7fSPeter Avalos 
8218de8d7fSPeter Avalos /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
8318de8d7fSPeter Avalos #ifdef PAM_SUN_CODEBASE
8418de8d7fSPeter Avalos # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
8518de8d7fSPeter Avalos #else
8618de8d7fSPeter Avalos # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
8718de8d7fSPeter Avalos #endif
8818de8d7fSPeter Avalos 
8918de8d7fSPeter Avalos #include "xmalloc.h"
90664f4763Szrj #include "sshbuf.h"
91664f4763Szrj #include "ssherr.h"
9218de8d7fSPeter Avalos #include "hostfile.h"
9318de8d7fSPeter Avalos #include "auth.h"
9418de8d7fSPeter Avalos #include "auth-pam.h"
9518de8d7fSPeter Avalos #include "canohost.h"
9618de8d7fSPeter Avalos #include "log.h"
9718de8d7fSPeter Avalos #include "msg.h"
9818de8d7fSPeter Avalos #include "packet.h"
9918de8d7fSPeter Avalos #include "misc.h"
10018de8d7fSPeter Avalos #include "servconf.h"
10118de8d7fSPeter Avalos #include "ssh2.h"
10218de8d7fSPeter Avalos #include "auth-options.h"
1030cbfa66cSDaniel Fojt #include "misc.h"
10418de8d7fSPeter Avalos #ifdef GSSAPI
10518de8d7fSPeter Avalos #include "ssh-gss.h"
10618de8d7fSPeter Avalos #endif
10718de8d7fSPeter Avalos #include "monitor_wrap.h"
10818de8d7fSPeter Avalos 
10918de8d7fSPeter Avalos extern ServerOptions options;
110664f4763Szrj extern struct sshbuf *loginmsg;
11118de8d7fSPeter Avalos extern u_int utmp_len;
11218de8d7fSPeter Avalos 
11318de8d7fSPeter Avalos /* so we don't silently change behaviour */
11418de8d7fSPeter Avalos #ifdef USE_POSIX_THREADS
11518de8d7fSPeter Avalos # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
11618de8d7fSPeter Avalos #endif
11718de8d7fSPeter Avalos 
11818de8d7fSPeter Avalos /*
11918de8d7fSPeter Avalos  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
12018de8d7fSPeter Avalos  * and generally a bad idea.  Use at own risk and do not expect support if
12118de8d7fSPeter Avalos  * this breaks.
12218de8d7fSPeter Avalos  */
12318de8d7fSPeter Avalos #ifdef UNSUPPORTED_POSIX_THREADS_HACK
12418de8d7fSPeter Avalos #include <pthread.h>
12518de8d7fSPeter Avalos /*
12618de8d7fSPeter Avalos  * Avoid namespace clash when *not* using pthreads for systems *with*
12718de8d7fSPeter Avalos  * pthreads, which unconditionally define pthread_t via sys/types.h
12818de8d7fSPeter Avalos  * (e.g. Linux)
12918de8d7fSPeter Avalos  */
13018de8d7fSPeter Avalos typedef pthread_t sp_pthread_t;
13118de8d7fSPeter Avalos #else
13218de8d7fSPeter Avalos typedef pid_t sp_pthread_t;
133664f4763Szrj #define pthread_exit	fake_pthread_exit
134664f4763Szrj #define pthread_create	fake_pthread_create
135664f4763Szrj #define pthread_cancel	fake_pthread_cancel
136664f4763Szrj #define pthread_join	fake_pthread_join
13718de8d7fSPeter Avalos #endif
13818de8d7fSPeter Avalos 
13918de8d7fSPeter Avalos struct pam_ctxt {
14018de8d7fSPeter Avalos 	sp_pthread_t	 pam_thread;
14118de8d7fSPeter Avalos 	int		 pam_psock;
14218de8d7fSPeter Avalos 	int		 pam_csock;
14318de8d7fSPeter Avalos 	int		 pam_done;
14418de8d7fSPeter Avalos };
14518de8d7fSPeter Avalos 
14618de8d7fSPeter Avalos static void sshpam_free_ctx(void *);
14718de8d7fSPeter Avalos static struct pam_ctxt *cleanup_ctxt;
14818de8d7fSPeter Avalos 
14918de8d7fSPeter Avalos #ifndef UNSUPPORTED_POSIX_THREADS_HACK
15018de8d7fSPeter Avalos /*
15118de8d7fSPeter Avalos  * Simulate threads with processes.
15218de8d7fSPeter Avalos  */
15318de8d7fSPeter Avalos 
15418de8d7fSPeter Avalos static int sshpam_thread_status = -1;
1550cbfa66cSDaniel Fojt static sshsig_t sshpam_oldsig;
15618de8d7fSPeter Avalos 
15718de8d7fSPeter Avalos static void
sshpam_sigchld_handler(int sig)15818de8d7fSPeter Avalos sshpam_sigchld_handler(int sig)
15918de8d7fSPeter Avalos {
1600cbfa66cSDaniel Fojt 	ssh_signal(SIGCHLD, SIG_DFL);
16118de8d7fSPeter Avalos 	if (cleanup_ctxt == NULL)
16218de8d7fSPeter Avalos 		return;	/* handler called after PAM cleanup, shouldn't happen */
16318de8d7fSPeter Avalos 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
16418de8d7fSPeter Avalos 	    <= 0) {
16518de8d7fSPeter Avalos 		/* PAM thread has not exitted, privsep slave must have */
16618de8d7fSPeter Avalos 		kill(cleanup_ctxt->pam_thread, SIGTERM);
167e9778795SPeter Avalos 		while (waitpid(cleanup_ctxt->pam_thread,
168e9778795SPeter Avalos 		    &sshpam_thread_status, 0) == -1) {
169e9778795SPeter Avalos 			if (errno == EINTR)
170e9778795SPeter Avalos 				continue;
171e9778795SPeter Avalos 			return;
172e9778795SPeter Avalos 		}
17318de8d7fSPeter Avalos 	}
17418de8d7fSPeter Avalos 	if (WIFSIGNALED(sshpam_thread_status) &&
17518de8d7fSPeter Avalos 	    WTERMSIG(sshpam_thread_status) == SIGTERM)
17618de8d7fSPeter Avalos 		return;	/* terminated by pthread_cancel */
17718de8d7fSPeter Avalos 	if (!WIFEXITED(sshpam_thread_status))
17818de8d7fSPeter Avalos 		sigdie("PAM: authentication thread exited unexpectedly");
17918de8d7fSPeter Avalos 	if (WEXITSTATUS(sshpam_thread_status) != 0)
18018de8d7fSPeter Avalos 		sigdie("PAM: authentication thread exited uncleanly");
18118de8d7fSPeter Avalos }
18218de8d7fSPeter Avalos 
18318de8d7fSPeter Avalos /* ARGSUSED */
18418de8d7fSPeter Avalos static void
pthread_exit(void * value)18518de8d7fSPeter Avalos pthread_exit(void *value)
18618de8d7fSPeter Avalos {
18718de8d7fSPeter Avalos 	_exit(0);
18818de8d7fSPeter Avalos }
18918de8d7fSPeter Avalos 
19018de8d7fSPeter Avalos /* ARGSUSED */
19118de8d7fSPeter Avalos static int
pthread_create(sp_pthread_t * thread,const void * attr,void * (* thread_start)(void *),void * arg)19218de8d7fSPeter Avalos pthread_create(sp_pthread_t *thread, const void *attr,
19318de8d7fSPeter Avalos     void *(*thread_start)(void *), void *arg)
19418de8d7fSPeter Avalos {
19518de8d7fSPeter Avalos 	pid_t pid;
19618de8d7fSPeter Avalos 	struct pam_ctxt *ctx = arg;
19718de8d7fSPeter Avalos 
19818de8d7fSPeter Avalos 	sshpam_thread_status = -1;
19918de8d7fSPeter Avalos 	switch ((pid = fork())) {
20018de8d7fSPeter Avalos 	case -1:
20118de8d7fSPeter Avalos 		error("fork(): %s", strerror(errno));
2020cbfa66cSDaniel Fojt 		return errno;
20318de8d7fSPeter Avalos 	case 0:
20418de8d7fSPeter Avalos 		close(ctx->pam_psock);
20518de8d7fSPeter Avalos 		ctx->pam_psock = -1;
20618de8d7fSPeter Avalos 		thread_start(arg);
20718de8d7fSPeter Avalos 		_exit(1);
20818de8d7fSPeter Avalos 	default:
20918de8d7fSPeter Avalos 		*thread = pid;
21018de8d7fSPeter Avalos 		close(ctx->pam_csock);
21118de8d7fSPeter Avalos 		ctx->pam_csock = -1;
2120cbfa66cSDaniel Fojt 		sshpam_oldsig = ssh_signal(SIGCHLD, sshpam_sigchld_handler);
21318de8d7fSPeter Avalos 		return (0);
21418de8d7fSPeter Avalos 	}
21518de8d7fSPeter Avalos }
21618de8d7fSPeter Avalos 
21718de8d7fSPeter Avalos static int
pthread_cancel(sp_pthread_t thread)21818de8d7fSPeter Avalos pthread_cancel(sp_pthread_t thread)
21918de8d7fSPeter Avalos {
2200cbfa66cSDaniel Fojt 	ssh_signal(SIGCHLD, sshpam_oldsig);
22118de8d7fSPeter Avalos 	return (kill(thread, SIGTERM));
22218de8d7fSPeter Avalos }
22318de8d7fSPeter Avalos 
22418de8d7fSPeter Avalos /* ARGSUSED */
22518de8d7fSPeter Avalos static int
pthread_join(sp_pthread_t thread,void ** value)22618de8d7fSPeter Avalos pthread_join(sp_pthread_t thread, void **value)
22718de8d7fSPeter Avalos {
22818de8d7fSPeter Avalos 	int status;
22918de8d7fSPeter Avalos 
23018de8d7fSPeter Avalos 	if (sshpam_thread_status != -1)
23118de8d7fSPeter Avalos 		return (sshpam_thread_status);
2320cbfa66cSDaniel Fojt 	ssh_signal(SIGCHLD, sshpam_oldsig);
233e9778795SPeter Avalos 	while (waitpid(thread, &status, 0) == -1) {
234e9778795SPeter Avalos 		if (errno == EINTR)
235e9778795SPeter Avalos 			continue;
236e9778795SPeter Avalos 		fatal("%s: waitpid: %s", __func__, strerror(errno));
237e9778795SPeter Avalos 	}
23818de8d7fSPeter Avalos 	return (status);
23918de8d7fSPeter Avalos }
24018de8d7fSPeter Avalos #endif
24118de8d7fSPeter Avalos 
24218de8d7fSPeter Avalos 
24318de8d7fSPeter Avalos static pam_handle_t *sshpam_handle = NULL;
24418de8d7fSPeter Avalos static int sshpam_err = 0;
24518de8d7fSPeter Avalos static int sshpam_authenticated = 0;
24618de8d7fSPeter Avalos static int sshpam_session_open = 0;
24718de8d7fSPeter Avalos static int sshpam_cred_established = 0;
24818de8d7fSPeter Avalos static int sshpam_account_status = -1;
249e9778795SPeter Avalos static int sshpam_maxtries_reached = 0;
25018de8d7fSPeter Avalos static char **sshpam_env = NULL;
25118de8d7fSPeter Avalos static Authctxt *sshpam_authctxt = NULL;
25218de8d7fSPeter Avalos static const char *sshpam_password = NULL;
253664f4763Szrj static char *sshpam_rhost = NULL;
254664f4763Szrj static char *sshpam_laddr = NULL;
255664f4763Szrj static char *sshpam_conninfo = NULL;
25618de8d7fSPeter Avalos 
25718de8d7fSPeter Avalos /* Some PAM implementations don't implement this */
25818de8d7fSPeter Avalos #ifndef HAVE_PAM_GETENVLIST
25918de8d7fSPeter Avalos static char **
pam_getenvlist(pam_handle_t * pamh)26018de8d7fSPeter Avalos pam_getenvlist(pam_handle_t *pamh)
26118de8d7fSPeter Avalos {
26218de8d7fSPeter Avalos 	/*
2630cbfa66cSDaniel Fojt 	 * XXX - If necessary, we can still support environment passing
26418de8d7fSPeter Avalos 	 * for platforms without pam_getenvlist by searching for known
26518de8d7fSPeter Avalos 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
26618de8d7fSPeter Avalos 	 */
26718de8d7fSPeter Avalos 	 return NULL;
26818de8d7fSPeter Avalos }
26918de8d7fSPeter Avalos #endif
27018de8d7fSPeter Avalos 
2710cbfa66cSDaniel Fojt #ifndef HAVE_PAM_PUTENV
2720cbfa66cSDaniel Fojt static int
pam_putenv(pam_handle_t * pamh,const char * name_value)2730cbfa66cSDaniel Fojt pam_putenv(pam_handle_t *pamh, const char *name_value)
2740cbfa66cSDaniel Fojt {
2750cbfa66cSDaniel Fojt 	return PAM_SUCCESS;
2760cbfa66cSDaniel Fojt }
2770cbfa66cSDaniel Fojt #endif /* HAVE_PAM_PUTENV */
2780cbfa66cSDaniel Fojt 
27918de8d7fSPeter Avalos /*
28018de8d7fSPeter Avalos  * Some platforms, notably Solaris, do not enforce password complexity
28118de8d7fSPeter Avalos  * rules during pam_chauthtok() if the real uid of the calling process
28218de8d7fSPeter Avalos  * is 0, on the assumption that it's being called by "passwd" run by root.
28318de8d7fSPeter Avalos  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
28418de8d7fSPeter Avalos  * the right thing.
28518de8d7fSPeter Avalos  */
28618de8d7fSPeter Avalos #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
28718de8d7fSPeter Avalos static int
sshpam_chauthtok_ruid(pam_handle_t * pamh,int flags)28818de8d7fSPeter Avalos sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
28918de8d7fSPeter Avalos {
29018de8d7fSPeter Avalos 	int result;
29118de8d7fSPeter Avalos 
29218de8d7fSPeter Avalos 	if (sshpam_authctxt == NULL)
29318de8d7fSPeter Avalos 		fatal("PAM: sshpam_authctxt not initialized");
29418de8d7fSPeter Avalos 	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
29518de8d7fSPeter Avalos 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
29618de8d7fSPeter Avalos 	result = pam_chauthtok(pamh, flags);
29718de8d7fSPeter Avalos 	if (setreuid(0, -1) == -1)
29818de8d7fSPeter Avalos 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
29918de8d7fSPeter Avalos 	return result;
30018de8d7fSPeter Avalos }
30118de8d7fSPeter Avalos # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
30218de8d7fSPeter Avalos #endif
30318de8d7fSPeter Avalos 
3040cbfa66cSDaniel Fojt static void
sshpam_password_change_required(int reqd)30518de8d7fSPeter Avalos sshpam_password_change_required(int reqd)
30618de8d7fSPeter Avalos {
307664f4763Szrj 	extern struct sshauthopt *auth_opts;
308664f4763Szrj 	static int saved_port, saved_agent, saved_x11;
309664f4763Szrj 
31018de8d7fSPeter Avalos 	debug3("%s %d", __func__, reqd);
31118de8d7fSPeter Avalos 	if (sshpam_authctxt == NULL)
31218de8d7fSPeter Avalos 		fatal("%s: PAM authctxt not initialized", __func__);
31318de8d7fSPeter Avalos 	sshpam_authctxt->force_pwchange = reqd;
31418de8d7fSPeter Avalos 	if (reqd) {
315664f4763Szrj 		saved_port = auth_opts->permit_port_forwarding_flag;
316664f4763Szrj 		saved_agent = auth_opts->permit_agent_forwarding_flag;
317664f4763Szrj 		saved_x11 = auth_opts->permit_x11_forwarding_flag;
318664f4763Szrj 		auth_opts->permit_port_forwarding_flag = 0;
319664f4763Szrj 		auth_opts->permit_agent_forwarding_flag = 0;
320664f4763Szrj 		auth_opts->permit_x11_forwarding_flag = 0;
32118de8d7fSPeter Avalos 	} else {
322664f4763Szrj 		if (saved_port)
323664f4763Szrj 			auth_opts->permit_port_forwarding_flag = saved_port;
324664f4763Szrj 		if (saved_agent)
325664f4763Szrj 			auth_opts->permit_agent_forwarding_flag = saved_agent;
326664f4763Szrj 		if (saved_x11)
327664f4763Szrj 			auth_opts->permit_x11_forwarding_flag = saved_x11;
32818de8d7fSPeter Avalos 	}
32918de8d7fSPeter Avalos }
33018de8d7fSPeter Avalos 
33118de8d7fSPeter Avalos /* Import regular and PAM environment from subprocess */
33218de8d7fSPeter Avalos static void
import_environments(struct sshbuf * b)333664f4763Szrj import_environments(struct sshbuf *b)
33418de8d7fSPeter Avalos {
33518de8d7fSPeter Avalos 	char *env;
336664f4763Szrj 	u_int n, i, num_env;
337664f4763Szrj 	int r;
33818de8d7fSPeter Avalos 
33918de8d7fSPeter Avalos 	debug3("PAM: %s entering", __func__);
34018de8d7fSPeter Avalos 
34118de8d7fSPeter Avalos #ifndef UNSUPPORTED_POSIX_THREADS_HACK
34218de8d7fSPeter Avalos 	/* Import variables set by do_pam_account */
343664f4763Szrj 	if ((r = sshbuf_get_u32(b, &n)) != 0)
344664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
345664f4763Szrj 	if (n > INT_MAX)
346664f4763Szrj 		fatal("%s: invalid PAM account status %u", __func__, n);
347664f4763Szrj 	sshpam_account_status = (int)n;
348664f4763Szrj 	if ((r = sshbuf_get_u32(b, &n)) != 0)
349664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
350664f4763Szrj 	sshpam_password_change_required(n != 0);
35118de8d7fSPeter Avalos 
35218de8d7fSPeter Avalos 	/* Import environment from subprocess */
353664f4763Szrj 	if ((r = sshbuf_get_u32(b, &num_env)) != 0)
354664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
35518de8d7fSPeter Avalos 	if (num_env > 1024)
35618de8d7fSPeter Avalos 		fatal("%s: received %u environment variables, expected <= 1024",
35718de8d7fSPeter Avalos 		    __func__, num_env);
35818de8d7fSPeter Avalos 	sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env));
35918de8d7fSPeter Avalos 	debug3("PAM: num env strings %d", num_env);
360664f4763Szrj 	for(i = 0; i < num_env; i++) {
361664f4763Szrj 		if ((r = sshbuf_get_cstring(b, &(sshpam_env[i]), NULL)) != 0)
362664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
363664f4763Szrj 	}
36418de8d7fSPeter Avalos 	sshpam_env[num_env] = NULL;
36518de8d7fSPeter Avalos 
36618de8d7fSPeter Avalos 	/* Import PAM environment from subprocess */
367664f4763Szrj 	if ((r = sshbuf_get_u32(b, &num_env)) != 0)
368664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
36918de8d7fSPeter Avalos 	debug("PAM: num PAM env strings %d", num_env);
37018de8d7fSPeter Avalos 	for (i = 0; i < num_env; i++) {
371664f4763Szrj 		if ((r = sshbuf_get_cstring(b, &env, NULL)) != 0)
372664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
37318de8d7fSPeter Avalos 		/* Errors are not fatal here */
374664f4763Szrj 		if ((r = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
37518de8d7fSPeter Avalos 			error("PAM: pam_putenv: %s",
376664f4763Szrj 			    pam_strerror(sshpam_handle, r));
37718de8d7fSPeter Avalos 		}
37850a69bb5SSascha Wildner 		/*
37950a69bb5SSascha Wildner 		 * XXX this possibly leaks env because it is not documented
38050a69bb5SSascha Wildner 		 * what pam_putenv() does with it. Does it copy it? Does it
38150a69bb5SSascha Wildner 		 * take ownweship? We don't know, so it's safest just to leak.
38250a69bb5SSascha Wildner 		 */
38318de8d7fSPeter Avalos 	}
38418de8d7fSPeter Avalos #endif
38518de8d7fSPeter Avalos }
38618de8d7fSPeter Avalos 
38718de8d7fSPeter Avalos /*
38818de8d7fSPeter Avalos  * Conversation function for authentication thread.
38918de8d7fSPeter Avalos  */
39018de8d7fSPeter Avalos static int
sshpam_thread_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)39118de8d7fSPeter Avalos sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
39218de8d7fSPeter Avalos     struct pam_response **resp, void *data)
39318de8d7fSPeter Avalos {
394664f4763Szrj 	struct sshbuf *buffer;
39518de8d7fSPeter Avalos 	struct pam_ctxt *ctxt;
39618de8d7fSPeter Avalos 	struct pam_response *reply;
397664f4763Szrj 	int r, i;
398664f4763Szrj 	u_char status;
39918de8d7fSPeter Avalos 
40018de8d7fSPeter Avalos 	debug3("PAM: %s entering, %d messages", __func__, n);
40118de8d7fSPeter Avalos 	*resp = NULL;
40218de8d7fSPeter Avalos 
40318de8d7fSPeter Avalos 	if (data == NULL) {
40418de8d7fSPeter Avalos 		error("PAM: conversation function passed a null context");
40518de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
40618de8d7fSPeter Avalos 	}
40718de8d7fSPeter Avalos 	ctxt = data;
40818de8d7fSPeter Avalos 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
40918de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
41018de8d7fSPeter Avalos 
41118de8d7fSPeter Avalos 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
412664f4763Szrj 		return PAM_CONV_ERR;
413664f4763Szrj 	if ((buffer = sshbuf_new()) == NULL) {
414664f4763Szrj 		free(reply);
415664f4763Szrj 		return PAM_CONV_ERR;
416664f4763Szrj 	}
41718de8d7fSPeter Avalos 
41818de8d7fSPeter Avalos 	for (i = 0; i < n; ++i) {
41918de8d7fSPeter Avalos 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
42018de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_OFF:
42118de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_ON:
422664f4763Szrj 			if ((r = sshbuf_put_cstring(buffer,
423664f4763Szrj 			    PAM_MSG_MEMBER(msg, i, msg))) != 0)
424664f4763Szrj 				fatal("%s: buffer error: %s",
425664f4763Szrj 				    __func__, ssh_err(r));
42618de8d7fSPeter Avalos 			if (ssh_msg_send(ctxt->pam_csock,
427664f4763Szrj 			    PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1)
42818de8d7fSPeter Avalos 				goto fail;
429664f4763Szrj 
430664f4763Szrj 			if (ssh_msg_recv(ctxt->pam_csock, buffer) == -1)
43118de8d7fSPeter Avalos 				goto fail;
432664f4763Szrj 			if ((r = sshbuf_get_u8(buffer, &status)) != 0)
433664f4763Szrj 				fatal("%s: buffer error: %s",
434664f4763Szrj 				    __func__, ssh_err(r));
435664f4763Szrj 			if (status != PAM_AUTHTOK)
43618de8d7fSPeter Avalos 				goto fail;
437664f4763Szrj 			if ((r = sshbuf_get_cstring(buffer,
438664f4763Szrj 			    &reply[i].resp, NULL)) != 0)
439664f4763Szrj 				fatal("%s: buffer error: %s",
440664f4763Szrj 				    __func__, ssh_err(r));
44118de8d7fSPeter Avalos 			break;
44218de8d7fSPeter Avalos 		case PAM_ERROR_MSG:
44318de8d7fSPeter Avalos 		case PAM_TEXT_INFO:
444664f4763Szrj 			if ((r = sshbuf_put_cstring(buffer,
445664f4763Szrj 			    PAM_MSG_MEMBER(msg, i, msg))) != 0)
446664f4763Szrj 				fatal("%s: buffer error: %s",
447664f4763Szrj 				    __func__, ssh_err(r));
44818de8d7fSPeter Avalos 			if (ssh_msg_send(ctxt->pam_csock,
449664f4763Szrj 			    PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1)
45018de8d7fSPeter Avalos 				goto fail;
45118de8d7fSPeter Avalos 			break;
45218de8d7fSPeter Avalos 		default:
45318de8d7fSPeter Avalos 			goto fail;
45418de8d7fSPeter Avalos 		}
455664f4763Szrj 		sshbuf_reset(buffer);
45618de8d7fSPeter Avalos 	}
457664f4763Szrj 	sshbuf_free(buffer);
45818de8d7fSPeter Avalos 	*resp = reply;
45918de8d7fSPeter Avalos 	return (PAM_SUCCESS);
46018de8d7fSPeter Avalos 
46118de8d7fSPeter Avalos  fail:
46218de8d7fSPeter Avalos 	for(i = 0; i < n; i++) {
46336e94dc5SPeter Avalos 		free(reply[i].resp);
46418de8d7fSPeter Avalos 	}
46536e94dc5SPeter Avalos 	free(reply);
466664f4763Szrj 	sshbuf_free(buffer);
46718de8d7fSPeter Avalos 	return (PAM_CONV_ERR);
46818de8d7fSPeter Avalos }
46918de8d7fSPeter Avalos 
47018de8d7fSPeter Avalos /*
47118de8d7fSPeter Avalos  * Authentication thread.
47218de8d7fSPeter Avalos  */
47318de8d7fSPeter Avalos static void *
sshpam_thread(void * ctxtp)47418de8d7fSPeter Avalos sshpam_thread(void *ctxtp)
47518de8d7fSPeter Avalos {
47618de8d7fSPeter Avalos 	struct pam_ctxt *ctxt = ctxtp;
477664f4763Szrj 	struct sshbuf *buffer = NULL;
47818de8d7fSPeter Avalos 	struct pam_conv sshpam_conv;
479664f4763Szrj 	int r, flags = (options.permit_empty_passwd == 0 ?
48018de8d7fSPeter Avalos 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
48118de8d7fSPeter Avalos #ifndef UNSUPPORTED_POSIX_THREADS_HACK
48218de8d7fSPeter Avalos 	extern char **environ;
48318de8d7fSPeter Avalos 	char **env_from_pam;
48418de8d7fSPeter Avalos 	u_int i;
48518de8d7fSPeter Avalos 	const char *pam_user;
48618de8d7fSPeter Avalos 	const char **ptr_pam_user = &pam_user;
48718de8d7fSPeter Avalos 	char *tz = getenv("TZ");
48818de8d7fSPeter Avalos 
48936e94dc5SPeter Avalos 	sshpam_err = pam_get_item(sshpam_handle, PAM_USER,
49018de8d7fSPeter Avalos 	    (sshpam_const void **)ptr_pam_user);
49136e94dc5SPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
49236e94dc5SPeter Avalos 		goto auth_fail;
49318de8d7fSPeter Avalos 
49418de8d7fSPeter Avalos 	environ[0] = NULL;
49518de8d7fSPeter Avalos 	if (tz != NULL)
49618de8d7fSPeter Avalos 		if (setenv("TZ", tz, 1) == -1)
49718de8d7fSPeter Avalos 			error("PAM: could not set TZ environment: %s",
49818de8d7fSPeter Avalos 			    strerror(errno));
49918de8d7fSPeter Avalos 
50018de8d7fSPeter Avalos 	if (sshpam_authctxt != NULL) {
50118de8d7fSPeter Avalos 		setproctitle("%s [pam]",
50218de8d7fSPeter Avalos 		    sshpam_authctxt->valid ? pam_user : "unknown");
50318de8d7fSPeter Avalos 	}
50418de8d7fSPeter Avalos #endif
50518de8d7fSPeter Avalos 
50618de8d7fSPeter Avalos 	sshpam_conv.conv = sshpam_thread_conv;
50718de8d7fSPeter Avalos 	sshpam_conv.appdata_ptr = ctxt;
50818de8d7fSPeter Avalos 
50918de8d7fSPeter Avalos 	if (sshpam_authctxt == NULL)
51018de8d7fSPeter Avalos 		fatal("%s: PAM authctxt not initialized", __func__);
51118de8d7fSPeter Avalos 
512664f4763Szrj 	if ((buffer = sshbuf_new()) == NULL)
513664f4763Szrj 		fatal("%s: sshbuf_new failed", __func__);
514664f4763Szrj 
51518de8d7fSPeter Avalos 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
51618de8d7fSPeter Avalos 	    (const void *)&sshpam_conv);
51718de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
51818de8d7fSPeter Avalos 		goto auth_fail;
51918de8d7fSPeter Avalos 	sshpam_err = pam_authenticate(sshpam_handle, flags);
520e9778795SPeter Avalos 	if (sshpam_err == PAM_MAXTRIES)
521e9778795SPeter Avalos 		sshpam_set_maxtries_reached(1);
52218de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
52318de8d7fSPeter Avalos 		goto auth_fail;
52418de8d7fSPeter Avalos 
52518de8d7fSPeter Avalos 	if (!do_pam_account()) {
52618de8d7fSPeter Avalos 		sshpam_err = PAM_ACCT_EXPIRED;
52718de8d7fSPeter Avalos 		goto auth_fail;
52818de8d7fSPeter Avalos 	}
52918de8d7fSPeter Avalos 	if (sshpam_authctxt->force_pwchange) {
53018de8d7fSPeter Avalos 		sshpam_err = pam_chauthtok(sshpam_handle,
53118de8d7fSPeter Avalos 		    PAM_CHANGE_EXPIRED_AUTHTOK);
53218de8d7fSPeter Avalos 		if (sshpam_err != PAM_SUCCESS)
53318de8d7fSPeter Avalos 			goto auth_fail;
53418de8d7fSPeter Avalos 		sshpam_password_change_required(0);
53518de8d7fSPeter Avalos 	}
53618de8d7fSPeter Avalos 
537664f4763Szrj 	if ((r = sshbuf_put_cstring(buffer, "OK")) != 0)
538664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
53918de8d7fSPeter Avalos 
54018de8d7fSPeter Avalos #ifndef UNSUPPORTED_POSIX_THREADS_HACK
54118de8d7fSPeter Avalos 	/* Export variables set by do_pam_account */
542664f4763Szrj 	if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 ||
543664f4763Szrj 	    (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0)
544664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
54518de8d7fSPeter Avalos 
54618de8d7fSPeter Avalos 	/* Export any environment strings set in child */
547664f4763Szrj 	for (i = 0; environ[i] != NULL; i++) {
548664f4763Szrj 		/* Count */
549664f4763Szrj 		if (i > INT_MAX)
5500cbfa66cSDaniel Fojt 			fatal("%s: too many environment strings", __func__);
551664f4763Szrj 	}
552664f4763Szrj 	if ((r = sshbuf_put_u32(buffer, i)) != 0)
553664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
554664f4763Szrj 	for (i = 0; environ[i] != NULL; i++) {
555664f4763Szrj 		if ((r = sshbuf_put_cstring(buffer, environ[i])) != 0)
556664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
557664f4763Szrj 	}
55818de8d7fSPeter Avalos 	/* Export any environment strings set by PAM in child */
55918de8d7fSPeter Avalos 	env_from_pam = pam_getenvlist(sshpam_handle);
560664f4763Szrj 	for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) {
561664f4763Szrj 		/* Count */
562664f4763Szrj 		if (i > INT_MAX)
5630cbfa66cSDaniel Fojt 			fatal("%s: too many PAM environment strings", __func__);
564664f4763Szrj 	}
565664f4763Szrj 	if ((r = sshbuf_put_u32(buffer, i)) != 0)
566664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
567664f4763Szrj 	for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) {
568664f4763Szrj 		if ((r = sshbuf_put_cstring(buffer, env_from_pam[i])) != 0)
569664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
570664f4763Szrj 	}
57118de8d7fSPeter Avalos #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
57218de8d7fSPeter Avalos 
57318de8d7fSPeter Avalos 	/* XXX - can't do much about an error here */
574664f4763Szrj 	ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer);
575664f4763Szrj 	sshbuf_free(buffer);
57618de8d7fSPeter Avalos 	pthread_exit(NULL);
57718de8d7fSPeter Avalos 
57818de8d7fSPeter Avalos  auth_fail:
579664f4763Szrj 	if ((r = sshbuf_put_cstring(buffer,
580664f4763Szrj 	    pam_strerror(sshpam_handle, sshpam_err))) != 0)
581664f4763Szrj 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
58218de8d7fSPeter Avalos 	/* XXX - can't do much about an error here */
58318de8d7fSPeter Avalos 	if (sshpam_err == PAM_ACCT_EXPIRED)
584664f4763Szrj 		ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer);
585e9778795SPeter Avalos 	else if (sshpam_maxtries_reached)
586664f4763Szrj 		ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer);
58718de8d7fSPeter Avalos 	else
588664f4763Szrj 		ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, buffer);
589664f4763Szrj 	sshbuf_free(buffer);
59018de8d7fSPeter Avalos 	pthread_exit(NULL);
59118de8d7fSPeter Avalos 
59218de8d7fSPeter Avalos 	return (NULL); /* Avoid warning for non-pthread case */
59318de8d7fSPeter Avalos }
59418de8d7fSPeter Avalos 
59518de8d7fSPeter Avalos void
sshpam_thread_cleanup(void)59618de8d7fSPeter Avalos sshpam_thread_cleanup(void)
59718de8d7fSPeter Avalos {
59818de8d7fSPeter Avalos 	struct pam_ctxt *ctxt = cleanup_ctxt;
59918de8d7fSPeter Avalos 
60018de8d7fSPeter Avalos 	debug3("PAM: %s entering", __func__);
60118de8d7fSPeter Avalos 	if (ctxt != NULL && ctxt->pam_thread != 0) {
60218de8d7fSPeter Avalos 		pthread_cancel(ctxt->pam_thread);
60318de8d7fSPeter Avalos 		pthread_join(ctxt->pam_thread, NULL);
60418de8d7fSPeter Avalos 		close(ctxt->pam_psock);
60518de8d7fSPeter Avalos 		close(ctxt->pam_csock);
60618de8d7fSPeter Avalos 		memset(ctxt, 0, sizeof(*ctxt));
60718de8d7fSPeter Avalos 		cleanup_ctxt = NULL;
60818de8d7fSPeter Avalos 	}
60918de8d7fSPeter Avalos }
61018de8d7fSPeter Avalos 
61118de8d7fSPeter Avalos static int
sshpam_null_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)61218de8d7fSPeter Avalos sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
61318de8d7fSPeter Avalos     struct pam_response **resp, void *data)
61418de8d7fSPeter Avalos {
61518de8d7fSPeter Avalos 	debug3("PAM: %s entering, %d messages", __func__, n);
61618de8d7fSPeter Avalos 	return (PAM_CONV_ERR);
61718de8d7fSPeter Avalos }
61818de8d7fSPeter Avalos 
61918de8d7fSPeter Avalos static struct pam_conv null_conv = { sshpam_null_conv, NULL };
62018de8d7fSPeter Avalos 
62118de8d7fSPeter Avalos static int
sshpam_store_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)62218de8d7fSPeter Avalos sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
62318de8d7fSPeter Avalos     struct pam_response **resp, void *data)
62418de8d7fSPeter Avalos {
62518de8d7fSPeter Avalos 	struct pam_response *reply;
626664f4763Szrj 	int r, i;
62718de8d7fSPeter Avalos 
62818de8d7fSPeter Avalos 	debug3("PAM: %s called with %d messages", __func__, n);
62918de8d7fSPeter Avalos 	*resp = NULL;
63018de8d7fSPeter Avalos 
63118de8d7fSPeter Avalos 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
63218de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
63318de8d7fSPeter Avalos 
63418de8d7fSPeter Avalos 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
63518de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
63618de8d7fSPeter Avalos 
63718de8d7fSPeter Avalos 	for (i = 0; i < n; ++i) {
63818de8d7fSPeter Avalos 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
63918de8d7fSPeter Avalos 		case PAM_ERROR_MSG:
64018de8d7fSPeter Avalos 		case PAM_TEXT_INFO:
641664f4763Szrj 			if ((r = sshbuf_putf(loginmsg, "%s\n",
642664f4763Szrj 			    PAM_MSG_MEMBER(msg, i, msg))) != 0)
643664f4763Szrj 				fatal("%s: buffer error: %s",
644664f4763Szrj 				    __func__, ssh_err(r));
64518de8d7fSPeter Avalos 			reply[i].resp_retcode = PAM_SUCCESS;
64618de8d7fSPeter Avalos 			break;
64718de8d7fSPeter Avalos 		default:
64818de8d7fSPeter Avalos 			goto fail;
64918de8d7fSPeter Avalos 		}
65018de8d7fSPeter Avalos 	}
65118de8d7fSPeter Avalos 	*resp = reply;
65218de8d7fSPeter Avalos 	return (PAM_SUCCESS);
65318de8d7fSPeter Avalos 
65418de8d7fSPeter Avalos  fail:
65518de8d7fSPeter Avalos 	for(i = 0; i < n; i++) {
65636e94dc5SPeter Avalos 		free(reply[i].resp);
65718de8d7fSPeter Avalos 	}
65836e94dc5SPeter Avalos 	free(reply);
65918de8d7fSPeter Avalos 	return (PAM_CONV_ERR);
66018de8d7fSPeter Avalos }
66118de8d7fSPeter Avalos 
66218de8d7fSPeter Avalos static struct pam_conv store_conv = { sshpam_store_conv, NULL };
66318de8d7fSPeter Avalos 
66418de8d7fSPeter Avalos void
sshpam_cleanup(void)66518de8d7fSPeter Avalos sshpam_cleanup(void)
66618de8d7fSPeter Avalos {
66718de8d7fSPeter Avalos 	if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor()))
66818de8d7fSPeter Avalos 		return;
66918de8d7fSPeter Avalos 	debug("PAM: cleanup");
67018de8d7fSPeter Avalos 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
67118de8d7fSPeter Avalos 	if (sshpam_session_open) {
67218de8d7fSPeter Avalos 		debug("PAM: closing session");
67318de8d7fSPeter Avalos 		pam_close_session(sshpam_handle, PAM_SILENT);
67418de8d7fSPeter Avalos 		sshpam_session_open = 0;
67518de8d7fSPeter Avalos 	}
67640c002afSPeter Avalos 	if (sshpam_cred_established) {
67740c002afSPeter Avalos 		debug("PAM: deleting credentials");
67840c002afSPeter Avalos 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
67940c002afSPeter Avalos 		sshpam_cred_established = 0;
68040c002afSPeter Avalos 	}
68118de8d7fSPeter Avalos 	sshpam_authenticated = 0;
68218de8d7fSPeter Avalos 	pam_end(sshpam_handle, sshpam_err);
68318de8d7fSPeter Avalos 	sshpam_handle = NULL;
68418de8d7fSPeter Avalos }
68518de8d7fSPeter Avalos 
68618de8d7fSPeter Avalos static int
sshpam_init(struct ssh * ssh,Authctxt * authctxt)687664f4763Szrj sshpam_init(struct ssh *ssh, Authctxt *authctxt)
68818de8d7fSPeter Avalos {
689664f4763Szrj 	const char *pam_user, *user = authctxt->user;
69018de8d7fSPeter Avalos 	const char **ptr_pam_user = &pam_user;
69118de8d7fSPeter Avalos 
69250a69bb5SSascha Wildner #if defined(PAM_SUN_CODEBASE) && defined(PAM_MAX_RESP_SIZE)
69350a69bb5SSascha Wildner 	/* Protect buggy PAM implementations from excessively long usernames */
69450a69bb5SSascha Wildner 	if (strlen(user) >= PAM_MAX_RESP_SIZE)
69550a69bb5SSascha Wildner 		fatal("Username too long from %s port %d",
69650a69bb5SSascha Wildner 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
69750a69bb5SSascha Wildner #endif
698664f4763Szrj 	if (sshpam_handle == NULL) {
699664f4763Szrj 		if (ssh == NULL) {
700664f4763Szrj 			fatal("%s: called initially with no "
701664f4763Szrj 			    "packet context", __func__);
702664f4763Szrj 		}
703664f4763Szrj 	} if (sshpam_handle != NULL) {
70418de8d7fSPeter Avalos 		/* We already have a PAM context; check if the user matches */
70518de8d7fSPeter Avalos 		sshpam_err = pam_get_item(sshpam_handle,
70618de8d7fSPeter Avalos 		    PAM_USER, (sshpam_const void **)ptr_pam_user);
70718de8d7fSPeter Avalos 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
70818de8d7fSPeter Avalos 			return (0);
70918de8d7fSPeter Avalos 		pam_end(sshpam_handle, sshpam_err);
71018de8d7fSPeter Avalos 		sshpam_handle = NULL;
71118de8d7fSPeter Avalos 	}
71218de8d7fSPeter Avalos 	debug("PAM: initializing for \"%s\"", user);
71318de8d7fSPeter Avalos 	sshpam_err =
71418de8d7fSPeter Avalos 	    pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
71518de8d7fSPeter Avalos 	sshpam_authctxt = authctxt;
71618de8d7fSPeter Avalos 
71718de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS) {
71818de8d7fSPeter Avalos 		pam_end(sshpam_handle, sshpam_err);
71918de8d7fSPeter Avalos 		sshpam_handle = NULL;
72018de8d7fSPeter Avalos 		return (-1);
72118de8d7fSPeter Avalos 	}
722664f4763Szrj 
723664f4763Szrj 	if (ssh != NULL && sshpam_rhost == NULL) {
724664f4763Szrj 		/*
725664f4763Szrj 		 * We need to cache these as we don't have packet context
726664f4763Szrj 		 * during the kbdint flow.
727664f4763Szrj 		 */
728664f4763Szrj 		sshpam_rhost = xstrdup(auth_get_canonical_hostname(ssh,
729664f4763Szrj 		    options.use_dns));
730664f4763Szrj 		sshpam_laddr = get_local_ipaddr(
731664f4763Szrj 		    ssh_packet_get_connection_in(ssh));
732664f4763Szrj 		xasprintf(&sshpam_conninfo, "SSH_CONNECTION=%.50s %d %.50s %d",
733664f4763Szrj 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
734664f4763Szrj 		    sshpam_laddr, ssh_local_port(ssh));
735664f4763Szrj 	}
736664f4763Szrj 	if (sshpam_rhost != NULL) {
737664f4763Szrj 		debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost);
738664f4763Szrj 		sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST,
739664f4763Szrj 		    sshpam_rhost);
74018de8d7fSPeter Avalos 		if (sshpam_err != PAM_SUCCESS) {
74118de8d7fSPeter Avalos 			pam_end(sshpam_handle, sshpam_err);
74218de8d7fSPeter Avalos 			sshpam_handle = NULL;
74318de8d7fSPeter Avalos 			return (-1);
74418de8d7fSPeter Avalos 		}
745664f4763Szrj 		/* Put SSH_CONNECTION in the PAM environment too */
746664f4763Szrj 		pam_putenv(sshpam_handle, sshpam_conninfo);
747664f4763Szrj 	}
748664f4763Szrj 
74918de8d7fSPeter Avalos #ifdef PAM_TTY_KLUDGE
75018de8d7fSPeter Avalos 	/*
75118de8d7fSPeter Avalos 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
75218de8d7fSPeter Avalos 	 * sshd doesn't set the tty until too late in the auth process and
75318de8d7fSPeter Avalos 	 * may not even set one (for tty-less connections)
75418de8d7fSPeter Avalos 	 */
75518de8d7fSPeter Avalos 	debug("PAM: setting PAM_TTY to \"ssh\"");
75618de8d7fSPeter Avalos 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
75718de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS) {
75818de8d7fSPeter Avalos 		pam_end(sshpam_handle, sshpam_err);
75918de8d7fSPeter Avalos 		sshpam_handle = NULL;
76018de8d7fSPeter Avalos 		return (-1);
76118de8d7fSPeter Avalos 	}
76218de8d7fSPeter Avalos #endif
76318de8d7fSPeter Avalos 	return (0);
76418de8d7fSPeter Avalos }
76518de8d7fSPeter Avalos 
766664f4763Szrj static void
expose_authinfo(const char * caller)767664f4763Szrj expose_authinfo(const char *caller)
768664f4763Szrj {
769664f4763Szrj 	char *auth_info;
770664f4763Szrj 
771664f4763Szrj 	/*
772664f4763Szrj 	 * Expose authentication information to PAM.
773664f4763Szrj 	 * The environment variable is versioned. Please increment the
774664f4763Szrj 	 * version suffix if the format of session_info changes.
775664f4763Szrj 	 */
776664f4763Szrj 	if (sshpam_authctxt->session_info == NULL)
777664f4763Szrj 		auth_info = xstrdup("");
778664f4763Szrj 	else if ((auth_info = sshbuf_dup_string(
779664f4763Szrj 	    sshpam_authctxt->session_info)) == NULL)
780664f4763Szrj 		fatal("%s: sshbuf_dup_string failed", __func__);
781664f4763Szrj 
782664f4763Szrj 	debug2("%s: auth information in SSH_AUTH_INFO_0", caller);
783664f4763Szrj 	do_pam_putenv("SSH_AUTH_INFO_0", auth_info);
784664f4763Szrj 	free(auth_info);
785664f4763Szrj }
786664f4763Szrj 
78718de8d7fSPeter Avalos static void *
sshpam_init_ctx(Authctxt * authctxt)78818de8d7fSPeter Avalos sshpam_init_ctx(Authctxt *authctxt)
78918de8d7fSPeter Avalos {
79018de8d7fSPeter Avalos 	struct pam_ctxt *ctxt;
7910cbfa66cSDaniel Fojt 	int result, socks[2];
79218de8d7fSPeter Avalos 
79318de8d7fSPeter Avalos 	debug3("PAM: %s entering", __func__);
79418de8d7fSPeter Avalos 	/*
79518de8d7fSPeter Avalos 	 * Refuse to start if we don't have PAM enabled or do_pam_account
79618de8d7fSPeter Avalos 	 * has previously failed.
79718de8d7fSPeter Avalos 	 */
79818de8d7fSPeter Avalos 	if (!options.use_pam || sshpam_account_status == 0)
79918de8d7fSPeter Avalos 		return NULL;
80018de8d7fSPeter Avalos 
80118de8d7fSPeter Avalos 	/* Initialize PAM */
802664f4763Szrj 	if (sshpam_init(NULL, authctxt) == -1) {
80318de8d7fSPeter Avalos 		error("PAM: initialization failed");
80418de8d7fSPeter Avalos 		return (NULL);
80518de8d7fSPeter Avalos 	}
80618de8d7fSPeter Avalos 
807664f4763Szrj 	expose_authinfo(__func__);
80818de8d7fSPeter Avalos 	ctxt = xcalloc(1, sizeof *ctxt);
80918de8d7fSPeter Avalos 
81018de8d7fSPeter Avalos 	/* Start the authentication thread */
81118de8d7fSPeter Avalos 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
81218de8d7fSPeter Avalos 		error("PAM: failed create sockets: %s", strerror(errno));
81336e94dc5SPeter Avalos 		free(ctxt);
81418de8d7fSPeter Avalos 		return (NULL);
81518de8d7fSPeter Avalos 	}
81618de8d7fSPeter Avalos 	ctxt->pam_psock = socks[0];
81718de8d7fSPeter Avalos 	ctxt->pam_csock = socks[1];
8180cbfa66cSDaniel Fojt 	result = pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt);
8190cbfa66cSDaniel Fojt 	if (result != 0) {
82018de8d7fSPeter Avalos 		error("PAM: failed to start authentication thread: %s",
8210cbfa66cSDaniel Fojt 		    strerror(result));
82218de8d7fSPeter Avalos 		close(socks[0]);
82318de8d7fSPeter Avalos 		close(socks[1]);
82436e94dc5SPeter Avalos 		free(ctxt);
82518de8d7fSPeter Avalos 		return (NULL);
82618de8d7fSPeter Avalos 	}
82718de8d7fSPeter Avalos 	cleanup_ctxt = ctxt;
82818de8d7fSPeter Avalos 	return (ctxt);
82918de8d7fSPeter Avalos }
83018de8d7fSPeter Avalos 
83118de8d7fSPeter Avalos static int
sshpam_query(void * ctx,char ** name,char ** info,u_int * num,char *** prompts,u_int ** echo_on)83218de8d7fSPeter Avalos sshpam_query(void *ctx, char **name, char **info,
83318de8d7fSPeter Avalos     u_int *num, char ***prompts, u_int **echo_on)
83418de8d7fSPeter Avalos {
835664f4763Szrj 	struct sshbuf *buffer;
83618de8d7fSPeter Avalos 	struct pam_ctxt *ctxt = ctx;
83718de8d7fSPeter Avalos 	size_t plen;
83818de8d7fSPeter Avalos 	u_char type;
83918de8d7fSPeter Avalos 	char *msg;
84018de8d7fSPeter Avalos 	size_t len, mlen;
841664f4763Szrj 	int r;
84218de8d7fSPeter Avalos 
84318de8d7fSPeter Avalos 	debug3("PAM: %s entering", __func__);
844664f4763Szrj 	if ((buffer = sshbuf_new()) == NULL)
845664f4763Szrj 		fatal("%s: sshbuf_new failed", __func__);
84618de8d7fSPeter Avalos 	*name = xstrdup("");
84718de8d7fSPeter Avalos 	*info = xstrdup("");
84818de8d7fSPeter Avalos 	*prompts = xmalloc(sizeof(char *));
84918de8d7fSPeter Avalos 	**prompts = NULL;
85018de8d7fSPeter Avalos 	plen = 0;
85118de8d7fSPeter Avalos 	*echo_on = xmalloc(sizeof(u_int));
852664f4763Szrj 	while (ssh_msg_recv(ctxt->pam_psock, buffer) == 0) {
853664f4763Szrj 		if ((r = sshbuf_get_u8(buffer, &type)) != 0 ||
854664f4763Szrj 		    (r = sshbuf_get_cstring(buffer, &msg, &mlen)) != 0)
855664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
85618de8d7fSPeter Avalos 		switch (type) {
85718de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_ON:
85818de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_OFF:
85918de8d7fSPeter Avalos 			*num = 1;
86018de8d7fSPeter Avalos 			len = plen + mlen + 1;
861e9778795SPeter Avalos 			**prompts = xreallocarray(**prompts, 1, len);
86218de8d7fSPeter Avalos 			strlcpy(**prompts + plen, msg, len - plen);
86318de8d7fSPeter Avalos 			plen += mlen;
86418de8d7fSPeter Avalos 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
86536e94dc5SPeter Avalos 			free(msg);
8660cbfa66cSDaniel Fojt 			sshbuf_free(buffer);
86718de8d7fSPeter Avalos 			return (0);
86818de8d7fSPeter Avalos 		case PAM_ERROR_MSG:
86918de8d7fSPeter Avalos 		case PAM_TEXT_INFO:
87018de8d7fSPeter Avalos 			/* accumulate messages */
87118de8d7fSPeter Avalos 			len = plen + mlen + 2;
872e9778795SPeter Avalos 			**prompts = xreallocarray(**prompts, 1, len);
87318de8d7fSPeter Avalos 			strlcpy(**prompts + plen, msg, len - plen);
87418de8d7fSPeter Avalos 			plen += mlen;
87518de8d7fSPeter Avalos 			strlcat(**prompts + plen, "\n", len - plen);
87618de8d7fSPeter Avalos 			plen++;
87736e94dc5SPeter Avalos 			free(msg);
87818de8d7fSPeter Avalos 			break;
87918de8d7fSPeter Avalos 		case PAM_ACCT_EXPIRED:
880e9778795SPeter Avalos 		case PAM_MAXTRIES:
881e9778795SPeter Avalos 			if (type == PAM_ACCT_EXPIRED)
88218de8d7fSPeter Avalos 				sshpam_account_status = 0;
883e9778795SPeter Avalos 			if (type == PAM_MAXTRIES)
884e9778795SPeter Avalos 				sshpam_set_maxtries_reached(1);
88518de8d7fSPeter Avalos 			/* FALLTHROUGH */
88618de8d7fSPeter Avalos 		case PAM_AUTH_ERR:
88718de8d7fSPeter Avalos 			debug3("PAM: %s", pam_strerror(sshpam_handle, type));
88818de8d7fSPeter Avalos 			if (**prompts != NULL && strlen(**prompts) != 0) {
88950a69bb5SSascha Wildner 				free(*info);
89018de8d7fSPeter Avalos 				*info = **prompts;
89118de8d7fSPeter Avalos 				**prompts = NULL;
89218de8d7fSPeter Avalos 				*num = 0;
89318de8d7fSPeter Avalos 				**echo_on = 0;
89418de8d7fSPeter Avalos 				ctxt->pam_done = -1;
89536e94dc5SPeter Avalos 				free(msg);
8960cbfa66cSDaniel Fojt 				sshbuf_free(buffer);
89718de8d7fSPeter Avalos 				return 0;
89818de8d7fSPeter Avalos 			}
89918de8d7fSPeter Avalos 			/* FALLTHROUGH */
90018de8d7fSPeter Avalos 		case PAM_SUCCESS:
90118de8d7fSPeter Avalos 			if (**prompts != NULL) {
90218de8d7fSPeter Avalos 				/* drain any accumulated messages */
90318de8d7fSPeter Avalos 				debug("PAM: %s", **prompts);
904664f4763Szrj 				if ((r = sshbuf_put(loginmsg, **prompts,
905664f4763Szrj 				    strlen(**prompts))) != 0)
906664f4763Szrj 					fatal("%s: buffer error: %s",
907664f4763Szrj 					    __func__, ssh_err(r));
90836e94dc5SPeter Avalos 				free(**prompts);
90918de8d7fSPeter Avalos 				**prompts = NULL;
91018de8d7fSPeter Avalos 			}
91118de8d7fSPeter Avalos 			if (type == PAM_SUCCESS) {
91218de8d7fSPeter Avalos 				if (!sshpam_authctxt->valid ||
91318de8d7fSPeter Avalos 				    (sshpam_authctxt->pw->pw_uid == 0 &&
91418de8d7fSPeter Avalos 				    options.permit_root_login != PERMIT_YES))
91518de8d7fSPeter Avalos 					fatal("Internal error: PAM auth "
91618de8d7fSPeter Avalos 					    "succeeded when it should have "
91718de8d7fSPeter Avalos 					    "failed");
918664f4763Szrj 				import_environments(buffer);
91918de8d7fSPeter Avalos 				*num = 0;
92018de8d7fSPeter Avalos 				**echo_on = 0;
92118de8d7fSPeter Avalos 				ctxt->pam_done = 1;
92236e94dc5SPeter Avalos 				free(msg);
9230cbfa66cSDaniel Fojt 				sshbuf_free(buffer);
92418de8d7fSPeter Avalos 				return (0);
92518de8d7fSPeter Avalos 			}
92618de8d7fSPeter Avalos 			error("PAM: %s for %s%.100s from %.100s", msg,
92718de8d7fSPeter Avalos 			    sshpam_authctxt->valid ? "" : "illegal user ",
928664f4763Szrj 			    sshpam_authctxt->user, sshpam_rhost);
92918de8d7fSPeter Avalos 			/* FALLTHROUGH */
93018de8d7fSPeter Avalos 		default:
93118de8d7fSPeter Avalos 			*num = 0;
93218de8d7fSPeter Avalos 			**echo_on = 0;
93336e94dc5SPeter Avalos 			free(msg);
93418de8d7fSPeter Avalos 			ctxt->pam_done = -1;
9350cbfa66cSDaniel Fojt 			sshbuf_free(buffer);
93618de8d7fSPeter Avalos 			return (-1);
93718de8d7fSPeter Avalos 		}
93818de8d7fSPeter Avalos 	}
9390cbfa66cSDaniel Fojt 	sshbuf_free(buffer);
94018de8d7fSPeter Avalos 	return (-1);
94118de8d7fSPeter Avalos }
94218de8d7fSPeter Avalos 
943e9778795SPeter Avalos /*
944e9778795SPeter Avalos  * Returns a junk password of identical length to that the user supplied.
945e9778795SPeter Avalos  * Used to mitigate timing attacks against crypt(3)/PAM stacks that
946e9778795SPeter Avalos  * vary processing time in proportion to password length.
947e9778795SPeter Avalos  */
948e9778795SPeter Avalos static char *
fake_password(const char * wire_password)949e9778795SPeter Avalos fake_password(const char *wire_password)
950e9778795SPeter Avalos {
951e9778795SPeter Avalos 	const char junk[] = "\b\n\r\177INCORRECT";
952e9778795SPeter Avalos 	char *ret = NULL;
953e9778795SPeter Avalos 	size_t i, l = wire_password != NULL ? strlen(wire_password) : 0;
954e9778795SPeter Avalos 
955e9778795SPeter Avalos 	if (l >= INT_MAX)
956e9778795SPeter Avalos 		fatal("%s: password length too long: %zu", __func__, l);
957e9778795SPeter Avalos 
958e9778795SPeter Avalos 	ret = malloc(l + 1);
959ce74bacaSMatthew Dillon 	if (ret == NULL)
960ce74bacaSMatthew Dillon 		return NULL;
961e9778795SPeter Avalos 	for (i = 0; i < l; i++)
962e9778795SPeter Avalos 		ret[i] = junk[i % (sizeof(junk) - 1)];
963e9778795SPeter Avalos 	ret[i] = '\0';
964e9778795SPeter Avalos 	return ret;
965e9778795SPeter Avalos }
966e9778795SPeter Avalos 
96718de8d7fSPeter Avalos /* XXX - see also comment in auth-chall.c:verify_response */
96818de8d7fSPeter Avalos static int
sshpam_respond(void * ctx,u_int num,char ** resp)96918de8d7fSPeter Avalos sshpam_respond(void *ctx, u_int num, char **resp)
97018de8d7fSPeter Avalos {
971664f4763Szrj 	struct sshbuf *buffer;
97218de8d7fSPeter Avalos 	struct pam_ctxt *ctxt = ctx;
973e9778795SPeter Avalos 	char *fake;
974664f4763Szrj 	int r;
97518de8d7fSPeter Avalos 
97618de8d7fSPeter Avalos 	debug2("PAM: %s entering, %u responses", __func__, num);
97718de8d7fSPeter Avalos 	switch (ctxt->pam_done) {
97818de8d7fSPeter Avalos 	case 1:
97918de8d7fSPeter Avalos 		sshpam_authenticated = 1;
98018de8d7fSPeter Avalos 		return (0);
98118de8d7fSPeter Avalos 	case 0:
98218de8d7fSPeter Avalos 		break;
98318de8d7fSPeter Avalos 	default:
98418de8d7fSPeter Avalos 		return (-1);
98518de8d7fSPeter Avalos 	}
98618de8d7fSPeter Avalos 	if (num != 1) {
98718de8d7fSPeter Avalos 		error("PAM: expected one response, got %u", num);
98818de8d7fSPeter Avalos 		return (-1);
98918de8d7fSPeter Avalos 	}
990664f4763Szrj 	if ((buffer = sshbuf_new()) == NULL)
991664f4763Szrj 		fatal("%s: sshbuf_new failed", __func__);
99218de8d7fSPeter Avalos 	if (sshpam_authctxt->valid &&
99318de8d7fSPeter Avalos 	    (sshpam_authctxt->pw->pw_uid != 0 ||
994664f4763Szrj 	    options.permit_root_login == PERMIT_YES)) {
995664f4763Szrj 		if ((r = sshbuf_put_cstring(buffer, *resp)) != 0)
996664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
997664f4763Szrj 	} else {
998e9778795SPeter Avalos 		fake = fake_password(*resp);
999664f4763Szrj 		if ((r = sshbuf_put_cstring(buffer, fake)) != 0)
1000664f4763Szrj 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
1001e9778795SPeter Avalos 		free(fake);
1002e9778795SPeter Avalos 	}
1003664f4763Szrj 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, buffer) == -1) {
1004664f4763Szrj 		sshbuf_free(buffer);
100518de8d7fSPeter Avalos 		return (-1);
100618de8d7fSPeter Avalos 	}
1007664f4763Szrj 	sshbuf_free(buffer);
100818de8d7fSPeter Avalos 	return (1);
100918de8d7fSPeter Avalos }
101018de8d7fSPeter Avalos 
101118de8d7fSPeter Avalos static void
sshpam_free_ctx(void * ctxtp)101218de8d7fSPeter Avalos sshpam_free_ctx(void *ctxtp)
101318de8d7fSPeter Avalos {
101418de8d7fSPeter Avalos 	struct pam_ctxt *ctxt = ctxtp;
101518de8d7fSPeter Avalos 
101618de8d7fSPeter Avalos 	debug3("PAM: %s entering", __func__);
101718de8d7fSPeter Avalos 	sshpam_thread_cleanup();
101836e94dc5SPeter Avalos 	free(ctxt);
101918de8d7fSPeter Avalos 	/*
102018de8d7fSPeter Avalos 	 * We don't call sshpam_cleanup() here because we may need the PAM
102118de8d7fSPeter Avalos 	 * handle at a later stage, e.g. when setting up a session.  It's
102218de8d7fSPeter Avalos 	 * still on the cleanup list, so pam_end() *will* be called before
102318de8d7fSPeter Avalos 	 * the server process terminates.
102418de8d7fSPeter Avalos 	 */
102518de8d7fSPeter Avalos }
102618de8d7fSPeter Avalos 
102718de8d7fSPeter Avalos KbdintDevice sshpam_device = {
102818de8d7fSPeter Avalos 	"pam",
102918de8d7fSPeter Avalos 	sshpam_init_ctx,
103018de8d7fSPeter Avalos 	sshpam_query,
103118de8d7fSPeter Avalos 	sshpam_respond,
103218de8d7fSPeter Avalos 	sshpam_free_ctx
103318de8d7fSPeter Avalos };
103418de8d7fSPeter Avalos 
103518de8d7fSPeter Avalos KbdintDevice mm_sshpam_device = {
103618de8d7fSPeter Avalos 	"pam",
103718de8d7fSPeter Avalos 	mm_sshpam_init_ctx,
103818de8d7fSPeter Avalos 	mm_sshpam_query,
103918de8d7fSPeter Avalos 	mm_sshpam_respond,
104018de8d7fSPeter Avalos 	mm_sshpam_free_ctx
104118de8d7fSPeter Avalos };
104218de8d7fSPeter Avalos 
104318de8d7fSPeter Avalos /*
104418de8d7fSPeter Avalos  * This replaces auth-pam.c
104518de8d7fSPeter Avalos  */
104618de8d7fSPeter Avalos void
start_pam(struct ssh * ssh)1047664f4763Szrj start_pam(struct ssh *ssh)
104818de8d7fSPeter Avalos {
1049664f4763Szrj 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1050664f4763Szrj 
105118de8d7fSPeter Avalos 	if (!options.use_pam)
105218de8d7fSPeter Avalos 		fatal("PAM: initialisation requested when UsePAM=no");
105318de8d7fSPeter Avalos 
1054664f4763Szrj 	if (sshpam_init(ssh, authctxt) == -1)
105518de8d7fSPeter Avalos 		fatal("PAM: initialisation failed");
105618de8d7fSPeter Avalos }
105718de8d7fSPeter Avalos 
105818de8d7fSPeter Avalos void
finish_pam(void)105918de8d7fSPeter Avalos finish_pam(void)
106018de8d7fSPeter Avalos {
106118de8d7fSPeter Avalos 	sshpam_cleanup();
106218de8d7fSPeter Avalos }
106318de8d7fSPeter Avalos 
1064ce74bacaSMatthew Dillon 
106518de8d7fSPeter Avalos u_int
do_pam_account(void)106618de8d7fSPeter Avalos do_pam_account(void)
106718de8d7fSPeter Avalos {
106818de8d7fSPeter Avalos 	debug("%s: called", __func__);
106918de8d7fSPeter Avalos 	if (sshpam_account_status != -1)
107018de8d7fSPeter Avalos 		return (sshpam_account_status);
107118de8d7fSPeter Avalos 
1072ce74bacaSMatthew Dillon 	expose_authinfo(__func__);
1073ce74bacaSMatthew Dillon 
107418de8d7fSPeter Avalos 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
107518de8d7fSPeter Avalos 	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
107618de8d7fSPeter Avalos 	    pam_strerror(sshpam_handle, sshpam_err));
107718de8d7fSPeter Avalos 
107818de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
107918de8d7fSPeter Avalos 		sshpam_account_status = 0;
108018de8d7fSPeter Avalos 		return (sshpam_account_status);
108118de8d7fSPeter Avalos 	}
108218de8d7fSPeter Avalos 
108318de8d7fSPeter Avalos 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
108418de8d7fSPeter Avalos 		sshpam_password_change_required(1);
108518de8d7fSPeter Avalos 
108618de8d7fSPeter Avalos 	sshpam_account_status = 1;
108718de8d7fSPeter Avalos 	return (sshpam_account_status);
108818de8d7fSPeter Avalos }
108918de8d7fSPeter Avalos 
109018de8d7fSPeter Avalos void
do_pam_setcred(int init)109118de8d7fSPeter Avalos do_pam_setcred(int init)
109218de8d7fSPeter Avalos {
109318de8d7fSPeter Avalos 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
109418de8d7fSPeter Avalos 	    (const void *)&store_conv);
109518de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
109618de8d7fSPeter Avalos 		fatal("PAM: failed to set PAM_CONV: %s",
109718de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
109818de8d7fSPeter Avalos 	if (init) {
109918de8d7fSPeter Avalos 		debug("PAM: establishing credentials");
110018de8d7fSPeter Avalos 		sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
110118de8d7fSPeter Avalos 	} else {
110218de8d7fSPeter Avalos 		debug("PAM: reinitializing credentials");
110318de8d7fSPeter Avalos 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
110418de8d7fSPeter Avalos 	}
110518de8d7fSPeter Avalos 	if (sshpam_err == PAM_SUCCESS) {
110618de8d7fSPeter Avalos 		sshpam_cred_established = 1;
110718de8d7fSPeter Avalos 		return;
110818de8d7fSPeter Avalos 	}
110918de8d7fSPeter Avalos 	if (sshpam_authenticated)
111018de8d7fSPeter Avalos 		fatal("PAM: pam_setcred(): %s",
111118de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
111218de8d7fSPeter Avalos 	else
111318de8d7fSPeter Avalos 		debug("PAM: pam_setcred(): %s",
111418de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
111518de8d7fSPeter Avalos }
111618de8d7fSPeter Avalos 
111718de8d7fSPeter Avalos static int
sshpam_tty_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)111818de8d7fSPeter Avalos sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
111918de8d7fSPeter Avalos     struct pam_response **resp, void *data)
112018de8d7fSPeter Avalos {
112118de8d7fSPeter Avalos 	char input[PAM_MAX_MSG_SIZE];
112218de8d7fSPeter Avalos 	struct pam_response *reply;
112318de8d7fSPeter Avalos 	int i;
112418de8d7fSPeter Avalos 
112518de8d7fSPeter Avalos 	debug3("PAM: %s called with %d messages", __func__, n);
112618de8d7fSPeter Avalos 
112718de8d7fSPeter Avalos 	*resp = NULL;
112818de8d7fSPeter Avalos 
112918de8d7fSPeter Avalos 	if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
113018de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
113118de8d7fSPeter Avalos 
113218de8d7fSPeter Avalos 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
113318de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
113418de8d7fSPeter Avalos 
113518de8d7fSPeter Avalos 	for (i = 0; i < n; ++i) {
113618de8d7fSPeter Avalos 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
113718de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_OFF:
113818de8d7fSPeter Avalos 			reply[i].resp =
113918de8d7fSPeter Avalos 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
114018de8d7fSPeter Avalos 			    RP_ALLOW_STDIN);
114118de8d7fSPeter Avalos 			reply[i].resp_retcode = PAM_SUCCESS;
114218de8d7fSPeter Avalos 			break;
114318de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_ON:
114418de8d7fSPeter Avalos 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
114518de8d7fSPeter Avalos 			if (fgets(input, sizeof input, stdin) == NULL)
114618de8d7fSPeter Avalos 				input[0] = '\0';
114718de8d7fSPeter Avalos 			if ((reply[i].resp = strdup(input)) == NULL)
114818de8d7fSPeter Avalos 				goto fail;
114918de8d7fSPeter Avalos 			reply[i].resp_retcode = PAM_SUCCESS;
115018de8d7fSPeter Avalos 			break;
115118de8d7fSPeter Avalos 		case PAM_ERROR_MSG:
115218de8d7fSPeter Avalos 		case PAM_TEXT_INFO:
115318de8d7fSPeter Avalos 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
115418de8d7fSPeter Avalos 			reply[i].resp_retcode = PAM_SUCCESS;
115518de8d7fSPeter Avalos 			break;
115618de8d7fSPeter Avalos 		default:
115718de8d7fSPeter Avalos 			goto fail;
115818de8d7fSPeter Avalos 		}
115918de8d7fSPeter Avalos 	}
116018de8d7fSPeter Avalos 	*resp = reply;
116118de8d7fSPeter Avalos 	return (PAM_SUCCESS);
116218de8d7fSPeter Avalos 
116318de8d7fSPeter Avalos  fail:
116418de8d7fSPeter Avalos 	for(i = 0; i < n; i++) {
116536e94dc5SPeter Avalos 		free(reply[i].resp);
116618de8d7fSPeter Avalos 	}
116736e94dc5SPeter Avalos 	free(reply);
116818de8d7fSPeter Avalos 	return (PAM_CONV_ERR);
116918de8d7fSPeter Avalos }
117018de8d7fSPeter Avalos 
117118de8d7fSPeter Avalos static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
117218de8d7fSPeter Avalos 
117318de8d7fSPeter Avalos /*
117418de8d7fSPeter Avalos  * XXX this should be done in the authentication phase, but ssh1 doesn't
117518de8d7fSPeter Avalos  * support that
117618de8d7fSPeter Avalos  */
117718de8d7fSPeter Avalos void
do_pam_chauthtok(void)117818de8d7fSPeter Avalos do_pam_chauthtok(void)
117918de8d7fSPeter Avalos {
118018de8d7fSPeter Avalos 	if (use_privsep)
118118de8d7fSPeter Avalos 		fatal("Password expired (unable to change with privsep)");
118218de8d7fSPeter Avalos 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
118318de8d7fSPeter Avalos 	    (const void *)&tty_conv);
118418de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
118518de8d7fSPeter Avalos 		fatal("PAM: failed to set PAM_CONV: %s",
118618de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
118718de8d7fSPeter Avalos 	debug("PAM: changing password");
118818de8d7fSPeter Avalos 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
118918de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
119018de8d7fSPeter Avalos 		fatal("PAM: pam_chauthtok(): %s",
119118de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
119218de8d7fSPeter Avalos }
119318de8d7fSPeter Avalos 
119418de8d7fSPeter Avalos void
do_pam_session(struct ssh * ssh)1195664f4763Szrj do_pam_session(struct ssh *ssh)
119618de8d7fSPeter Avalos {
119718de8d7fSPeter Avalos 	debug3("PAM: opening session");
1198ce74bacaSMatthew Dillon 
1199ce74bacaSMatthew Dillon 	expose_authinfo(__func__);
1200ce74bacaSMatthew Dillon 
120118de8d7fSPeter Avalos 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
120218de8d7fSPeter Avalos 	    (const void *)&store_conv);
120318de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
120418de8d7fSPeter Avalos 		fatal("PAM: failed to set PAM_CONV: %s",
120518de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
120618de8d7fSPeter Avalos 	sshpam_err = pam_open_session(sshpam_handle, 0);
120718de8d7fSPeter Avalos 	if (sshpam_err == PAM_SUCCESS)
120818de8d7fSPeter Avalos 		sshpam_session_open = 1;
120918de8d7fSPeter Avalos 	else {
121018de8d7fSPeter Avalos 		sshpam_session_open = 0;
1211664f4763Szrj 		auth_restrict_session(ssh);
121218de8d7fSPeter Avalos 		error("PAM: pam_open_session(): %s",
121318de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
121418de8d7fSPeter Avalos 	}
121518de8d7fSPeter Avalos 
121618de8d7fSPeter Avalos }
121718de8d7fSPeter Avalos 
121818de8d7fSPeter Avalos int
is_pam_session_open(void)121918de8d7fSPeter Avalos is_pam_session_open(void)
122018de8d7fSPeter Avalos {
122118de8d7fSPeter Avalos 	return sshpam_session_open;
122218de8d7fSPeter Avalos }
122318de8d7fSPeter Avalos 
122418de8d7fSPeter Avalos /*
122518de8d7fSPeter Avalos  * Set a PAM environment string. We need to do this so that the session
122618de8d7fSPeter Avalos  * modules can handle things like Kerberos/GSI credentials that appear
122718de8d7fSPeter Avalos  * during the ssh authentication process.
122818de8d7fSPeter Avalos  */
122918de8d7fSPeter Avalos int
do_pam_putenv(char * name,char * value)123018de8d7fSPeter Avalos do_pam_putenv(char *name, char *value)
123118de8d7fSPeter Avalos {
123218de8d7fSPeter Avalos 	int ret = 1;
123318de8d7fSPeter Avalos 	char *compound;
123418de8d7fSPeter Avalos 	size_t len;
123518de8d7fSPeter Avalos 
123618de8d7fSPeter Avalos 	len = strlen(name) + strlen(value) + 2;
123718de8d7fSPeter Avalos 	compound = xmalloc(len);
123818de8d7fSPeter Avalos 
123918de8d7fSPeter Avalos 	snprintf(compound, len, "%s=%s", name, value);
124018de8d7fSPeter Avalos 	ret = pam_putenv(sshpam_handle, compound);
124136e94dc5SPeter Avalos 	free(compound);
124218de8d7fSPeter Avalos 
124318de8d7fSPeter Avalos 	return (ret);
124418de8d7fSPeter Avalos }
124518de8d7fSPeter Avalos 
124618de8d7fSPeter Avalos char **
fetch_pam_child_environment(void)124718de8d7fSPeter Avalos fetch_pam_child_environment(void)
124818de8d7fSPeter Avalos {
124918de8d7fSPeter Avalos 	return sshpam_env;
125018de8d7fSPeter Avalos }
125118de8d7fSPeter Avalos 
125218de8d7fSPeter Avalos char **
fetch_pam_environment(void)125318de8d7fSPeter Avalos fetch_pam_environment(void)
125418de8d7fSPeter Avalos {
125518de8d7fSPeter Avalos 	return (pam_getenvlist(sshpam_handle));
125618de8d7fSPeter Avalos }
125718de8d7fSPeter Avalos 
125818de8d7fSPeter Avalos void
free_pam_environment(char ** env)125918de8d7fSPeter Avalos free_pam_environment(char **env)
126018de8d7fSPeter Avalos {
126118de8d7fSPeter Avalos 	char **envp;
126218de8d7fSPeter Avalos 
126318de8d7fSPeter Avalos 	if (env == NULL)
126418de8d7fSPeter Avalos 		return;
126518de8d7fSPeter Avalos 
126618de8d7fSPeter Avalos 	for (envp = env; *envp; envp++)
126736e94dc5SPeter Avalos 		free(*envp);
126836e94dc5SPeter Avalos 	free(env);
126918de8d7fSPeter Avalos }
127018de8d7fSPeter Avalos 
127118de8d7fSPeter Avalos /*
127218de8d7fSPeter Avalos  * "Blind" conversation function for password authentication.  Assumes that
127318de8d7fSPeter Avalos  * echo-off prompts are for the password and stores messages for later
127418de8d7fSPeter Avalos  * display.
127518de8d7fSPeter Avalos  */
127618de8d7fSPeter Avalos static int
sshpam_passwd_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)127718de8d7fSPeter Avalos sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
127818de8d7fSPeter Avalos     struct pam_response **resp, void *data)
127918de8d7fSPeter Avalos {
128018de8d7fSPeter Avalos 	struct pam_response *reply;
1281664f4763Szrj 	int r, i;
128218de8d7fSPeter Avalos 	size_t len;
128318de8d7fSPeter Avalos 
128418de8d7fSPeter Avalos 	debug3("PAM: %s called with %d messages", __func__, n);
128518de8d7fSPeter Avalos 
128618de8d7fSPeter Avalos 	*resp = NULL;
128718de8d7fSPeter Avalos 
128818de8d7fSPeter Avalos 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
128918de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
129018de8d7fSPeter Avalos 
129118de8d7fSPeter Avalos 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
129218de8d7fSPeter Avalos 		return (PAM_CONV_ERR);
129318de8d7fSPeter Avalos 
129418de8d7fSPeter Avalos 	for (i = 0; i < n; ++i) {
129518de8d7fSPeter Avalos 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
129618de8d7fSPeter Avalos 		case PAM_PROMPT_ECHO_OFF:
129718de8d7fSPeter Avalos 			if (sshpam_password == NULL)
129818de8d7fSPeter Avalos 				goto fail;
129918de8d7fSPeter Avalos 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
130018de8d7fSPeter Avalos 				goto fail;
130118de8d7fSPeter Avalos 			reply[i].resp_retcode = PAM_SUCCESS;
130218de8d7fSPeter Avalos 			break;
130318de8d7fSPeter Avalos 		case PAM_ERROR_MSG:
130418de8d7fSPeter Avalos 		case PAM_TEXT_INFO:
130518de8d7fSPeter Avalos 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
130618de8d7fSPeter Avalos 			if (len > 0) {
1307664f4763Szrj 				if ((r = sshbuf_putf(loginmsg, "%s\n",
1308664f4763Szrj 				    PAM_MSG_MEMBER(msg, i, msg))) != 0)
1309664f4763Szrj 					fatal("%s: buffer error: %s",
1310664f4763Szrj 					    __func__, ssh_err(r));
131118de8d7fSPeter Avalos 			}
131218de8d7fSPeter Avalos 			if ((reply[i].resp = strdup("")) == NULL)
131318de8d7fSPeter Avalos 				goto fail;
131418de8d7fSPeter Avalos 			reply[i].resp_retcode = PAM_SUCCESS;
131518de8d7fSPeter Avalos 			break;
131618de8d7fSPeter Avalos 		default:
131718de8d7fSPeter Avalos 			goto fail;
131818de8d7fSPeter Avalos 		}
131918de8d7fSPeter Avalos 	}
132018de8d7fSPeter Avalos 	*resp = reply;
132118de8d7fSPeter Avalos 	return (PAM_SUCCESS);
132218de8d7fSPeter Avalos 
132318de8d7fSPeter Avalos  fail:
132418de8d7fSPeter Avalos 	for(i = 0; i < n; i++) {
132536e94dc5SPeter Avalos 		free(reply[i].resp);
132618de8d7fSPeter Avalos 	}
132736e94dc5SPeter Avalos 	free(reply);
132818de8d7fSPeter Avalos 	return (PAM_CONV_ERR);
132918de8d7fSPeter Avalos }
133018de8d7fSPeter Avalos 
133118de8d7fSPeter Avalos static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
133218de8d7fSPeter Avalos 
133318de8d7fSPeter Avalos /*
133418de8d7fSPeter Avalos  * Attempt password authentication via PAM
133518de8d7fSPeter Avalos  */
133618de8d7fSPeter Avalos int
sshpam_auth_passwd(Authctxt * authctxt,const char * password)133718de8d7fSPeter Avalos sshpam_auth_passwd(Authctxt *authctxt, const char *password)
133818de8d7fSPeter Avalos {
133918de8d7fSPeter Avalos 	int flags = (options.permit_empty_passwd == 0 ?
134018de8d7fSPeter Avalos 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
1341e9778795SPeter Avalos 	char *fake = NULL;
134218de8d7fSPeter Avalos 
134318de8d7fSPeter Avalos 	if (!options.use_pam || sshpam_handle == NULL)
134418de8d7fSPeter Avalos 		fatal("PAM: %s called when PAM disabled or failed to "
134518de8d7fSPeter Avalos 		    "initialise.", __func__);
134618de8d7fSPeter Avalos 
134718de8d7fSPeter Avalos 	sshpam_password = password;
134818de8d7fSPeter Avalos 	sshpam_authctxt = authctxt;
134918de8d7fSPeter Avalos 
135018de8d7fSPeter Avalos 	/*
135118de8d7fSPeter Avalos 	 * If the user logging in is invalid, or is root but is not permitted
135218de8d7fSPeter Avalos 	 * by PermitRootLogin, use an invalid password to prevent leaking
135318de8d7fSPeter Avalos 	 * information via timing (eg if the PAM config has a delay on fail).
135418de8d7fSPeter Avalos 	 */
135518de8d7fSPeter Avalos 	if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
135618de8d7fSPeter Avalos 	    options.permit_root_login != PERMIT_YES))
1357e9778795SPeter Avalos 		sshpam_password = fake = fake_password(password);
135818de8d7fSPeter Avalos 
135918de8d7fSPeter Avalos 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
136018de8d7fSPeter Avalos 	    (const void *)&passwd_conv);
136118de8d7fSPeter Avalos 	if (sshpam_err != PAM_SUCCESS)
136218de8d7fSPeter Avalos 		fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
136318de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
136418de8d7fSPeter Avalos 
136518de8d7fSPeter Avalos 	sshpam_err = pam_authenticate(sshpam_handle, flags);
136618de8d7fSPeter Avalos 	sshpam_password = NULL;
1367e9778795SPeter Avalos 	free(fake);
1368e9778795SPeter Avalos 	if (sshpam_err == PAM_MAXTRIES)
1369e9778795SPeter Avalos 		sshpam_set_maxtries_reached(1);
137018de8d7fSPeter Avalos 	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
137118de8d7fSPeter Avalos 		debug("PAM: password authentication accepted for %.100s",
137218de8d7fSPeter Avalos 		    authctxt->user);
137318de8d7fSPeter Avalos 		return 1;
137418de8d7fSPeter Avalos 	} else {
137518de8d7fSPeter Avalos 		debug("PAM: password authentication failed for %.100s: %s",
137618de8d7fSPeter Avalos 		    authctxt->valid ? authctxt->user : "an illegal user",
137718de8d7fSPeter Avalos 		    pam_strerror(sshpam_handle, sshpam_err));
137818de8d7fSPeter Avalos 		return 0;
137918de8d7fSPeter Avalos 	}
138018de8d7fSPeter Avalos }
1381e9778795SPeter Avalos 
1382e9778795SPeter Avalos int
sshpam_get_maxtries_reached(void)1383e9778795SPeter Avalos sshpam_get_maxtries_reached(void)
1384e9778795SPeter Avalos {
1385e9778795SPeter Avalos 	return sshpam_maxtries_reached;
1386e9778795SPeter Avalos }
1387e9778795SPeter Avalos 
1388e9778795SPeter Avalos void
sshpam_set_maxtries_reached(int reached)1389e9778795SPeter Avalos sshpam_set_maxtries_reached(int reached)
1390e9778795SPeter Avalos {
1391e9778795SPeter Avalos 	if (reached == 0 || sshpam_maxtries_reached)
1392e9778795SPeter Avalos 		return;
1393e9778795SPeter Avalos 	sshpam_maxtries_reached = 1;
1394e9778795SPeter Avalos 	options.password_authentication = 0;
1395e9778795SPeter Avalos 	options.kbd_interactive_authentication = 0;
1396e9778795SPeter Avalos }
139718de8d7fSPeter Avalos #endif /* USE_PAM */
1398