xref: /freebsd/crypto/openssh/auth-pam.c (revision 21e764df0c8084af2d7d6f5ecdaa136ad81246ed)
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"
5021e764dfSDag-Erling Smørgrav RCSID("$Id: auth-pam.c,v 1.114 2004/08/16 13:12:06 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 
60989dd127SDag-Erling Smørgrav #include "auth.h"
61989dd127SDag-Erling Smørgrav #include "auth-pam.h"
62cf2b5f3bSDag-Erling Smørgrav #include "buffer.h"
63cf2b5f3bSDag-Erling Smørgrav #include "bufaux.h"
644c5de869SBrian Feldman #include "canohost.h"
65cf2b5f3bSDag-Erling Smørgrav #include "log.h"
66cf2b5f3bSDag-Erling Smørgrav #include "monitor_wrap.h"
67cf2b5f3bSDag-Erling Smørgrav #include "msg.h"
68cf2b5f3bSDag-Erling Smørgrav #include "packet.h"
6921e764dfSDag-Erling Smørgrav #include "misc.h"
70cf2b5f3bSDag-Erling Smørgrav #include "servconf.h"
71cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h"
72cf2b5f3bSDag-Erling Smørgrav #include "xmalloc.h"
73cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h"
7409958426SBrian Feldman 
75989dd127SDag-Erling Smørgrav extern ServerOptions options;
761ec0d754SDag-Erling Smørgrav extern Buffer loginmsg;
771ec0d754SDag-Erling Smørgrav extern int compat20;
785962c0e9SDag-Erling Smørgrav extern u_int utmp_len;
792c917d39SAlfred Perlstein 
80cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS
81cf2b5f3bSDag-Erling Smørgrav #include <pthread.h>
82cf2b5f3bSDag-Erling Smørgrav /*
83cf2b5f3bSDag-Erling Smørgrav  * Avoid namespace clash when *not* using pthreads for systems *with*
84cf2b5f3bSDag-Erling Smørgrav  * pthreads, which unconditionally define pthread_t via sys/types.h
85cf2b5f3bSDag-Erling Smørgrav  * (e.g. Linux)
86cf2b5f3bSDag-Erling Smørgrav  */
87cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t;
88cf2b5f3bSDag-Erling Smørgrav #else
891ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t;
901ec0d754SDag-Erling Smørgrav #endif
911ec0d754SDag-Erling Smørgrav 
921ec0d754SDag-Erling Smørgrav struct pam_ctxt {
931ec0d754SDag-Erling Smørgrav 	sp_pthread_t	 pam_thread;
941ec0d754SDag-Erling Smørgrav 	int		 pam_psock;
951ec0d754SDag-Erling Smørgrav 	int		 pam_csock;
961ec0d754SDag-Erling Smørgrav 	int		 pam_done;
971ec0d754SDag-Erling Smørgrav };
981ec0d754SDag-Erling Smørgrav 
991ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *);
1001ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt;
1011ec0d754SDag-Erling Smørgrav 
1021ec0d754SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
103cf2b5f3bSDag-Erling Smørgrav /*
104cf2b5f3bSDag-Erling Smørgrav  * Simulate threads with processes.
105cf2b5f3bSDag-Erling Smørgrav  */
1061ec0d754SDag-Erling Smørgrav 
1071ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1;
1081ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig;
1091ec0d754SDag-Erling Smørgrav 
1101ec0d754SDag-Erling Smørgrav static void
1111ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig)
1121ec0d754SDag-Erling Smørgrav {
11321e764dfSDag-Erling Smørgrav 	signal(SIGCHLD, SIG_DFL);
1141ec0d754SDag-Erling Smørgrav 	if (cleanup_ctxt == NULL)
1151ec0d754SDag-Erling Smørgrav 		return;	/* handler called after PAM cleanup, shouldn't happen */
11621e764dfSDag-Erling Smørgrav 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
11721e764dfSDag-Erling Smørgrav 	     <= 0) {
11821e764dfSDag-Erling Smørgrav 		/* PAM thread has not exitted, privsep slave must have */
11921e764dfSDag-Erling Smørgrav 		kill(cleanup_ctxt->pam_thread, SIGTERM);
12021e764dfSDag-Erling Smørgrav 		if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
12121e764dfSDag-Erling Smørgrav 		    <= 0)
12221e764dfSDag-Erling Smørgrav 			return; /* could not wait */
12321e764dfSDag-Erling Smørgrav 	}
1241ec0d754SDag-Erling Smørgrav 	if (WIFSIGNALED(sshpam_thread_status) &&
1251ec0d754SDag-Erling Smørgrav 	    WTERMSIG(sshpam_thread_status) == SIGTERM)
1261ec0d754SDag-Erling Smørgrav 		return;	/* terminated by pthread_cancel */
1271ec0d754SDag-Erling Smørgrav 	if (!WIFEXITED(sshpam_thread_status))
1281ec0d754SDag-Erling Smørgrav 		fatal("PAM: authentication thread exited unexpectedly");
1291ec0d754SDag-Erling Smørgrav 	if (WEXITSTATUS(sshpam_thread_status) != 0)
1301ec0d754SDag-Erling Smørgrav 		fatal("PAM: authentication thread exited uncleanly");
1311ec0d754SDag-Erling Smørgrav }
13209958426SBrian Feldman 
133cf2b5f3bSDag-Erling Smørgrav static void
134cf2b5f3bSDag-Erling Smørgrav pthread_exit(void *value __unused)
13509958426SBrian Feldman {
136cf2b5f3bSDag-Erling Smørgrav 	_exit(0);
13709958426SBrian Feldman }
13809958426SBrian Feldman 
139cf2b5f3bSDag-Erling Smørgrav static int
140cf2b5f3bSDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr __unused,
141cf2b5f3bSDag-Erling Smørgrav     void *(*thread_start)(void *), void *arg)
142cf2b5f3bSDag-Erling Smørgrav {
143cf2b5f3bSDag-Erling Smørgrav 	pid_t pid;
144cf2b5f3bSDag-Erling Smørgrav 
1455962c0e9SDag-Erling Smørgrav 	sshpam_thread_status = -1;
146cf2b5f3bSDag-Erling Smørgrav 	switch ((pid = fork())) {
147cf2b5f3bSDag-Erling Smørgrav 	case -1:
148cf2b5f3bSDag-Erling Smørgrav 		error("fork(): %s", strerror(errno));
149cf2b5f3bSDag-Erling Smørgrav 		return (-1);
150cf2b5f3bSDag-Erling Smørgrav 	case 0:
151cf2b5f3bSDag-Erling Smørgrav 		thread_start(arg);
152cf2b5f3bSDag-Erling Smørgrav 		_exit(1);
153cf2b5f3bSDag-Erling Smørgrav 	default:
154cf2b5f3bSDag-Erling Smørgrav 		*thread = pid;
1551ec0d754SDag-Erling Smørgrav 		sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
156cf2b5f3bSDag-Erling Smørgrav 		return (0);
157cf2b5f3bSDag-Erling Smørgrav 	}
158cf2b5f3bSDag-Erling Smørgrav }
159cf2b5f3bSDag-Erling Smørgrav 
160cf2b5f3bSDag-Erling Smørgrav static int
161cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread)
162cf2b5f3bSDag-Erling Smørgrav {
1631ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
164cf2b5f3bSDag-Erling Smørgrav 	return (kill(thread, SIGTERM));
165cf2b5f3bSDag-Erling Smørgrav }
166cf2b5f3bSDag-Erling Smørgrav 
167cf2b5f3bSDag-Erling Smørgrav static int
168cf2b5f3bSDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value __unused)
169cf2b5f3bSDag-Erling Smørgrav {
170cf2b5f3bSDag-Erling Smørgrav 	int status;
171cf2b5f3bSDag-Erling Smørgrav 
1721ec0d754SDag-Erling Smørgrav 	if (sshpam_thread_status != -1)
1731ec0d754SDag-Erling Smørgrav 		return (sshpam_thread_status);
1741ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
175cf2b5f3bSDag-Erling Smørgrav 	waitpid(thread, &status, 0);
176cf2b5f3bSDag-Erling Smørgrav 	return (status);
177cf2b5f3bSDag-Erling Smørgrav }
178cf2b5f3bSDag-Erling Smørgrav #endif
179cf2b5f3bSDag-Erling Smørgrav 
180cf2b5f3bSDag-Erling Smørgrav 
181cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL;
182cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0;
183cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0;
184cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0;
185cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0;
1861ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1;
1871ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL;
1885962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL;
18921e764dfSDag-Erling Smørgrav static const char *sshpam_password = NULL;
190cf2b5f3bSDag-Erling Smørgrav 
1911ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */
1921ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST
1931ec0d754SDag-Erling Smørgrav static char **
1941ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh)
1951ec0d754SDag-Erling Smørgrav {
1961ec0d754SDag-Erling Smørgrav 	/*
1971ec0d754SDag-Erling Smørgrav 	 * XXX - If necessary, we can still support envrionment passing
1981ec0d754SDag-Erling Smørgrav 	 * for platforms without pam_getenvlist by searching for known
1991ec0d754SDag-Erling Smørgrav 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
2001ec0d754SDag-Erling Smørgrav 	 */
2011ec0d754SDag-Erling Smørgrav 	 return NULL;
2021ec0d754SDag-Erling Smørgrav }
2031ec0d754SDag-Erling Smørgrav #endif
204cf2b5f3bSDag-Erling Smørgrav 
20521e764dfSDag-Erling Smørgrav /*
20621e764dfSDag-Erling Smørgrav  * Some platforms, notably Solaris, do not enforce password complexity
20721e764dfSDag-Erling Smørgrav  * rules during pam_chauthtok() if the real uid of the calling process
20821e764dfSDag-Erling Smørgrav  * is 0, on the assumption that it's being called by "passwd" run by root.
20921e764dfSDag-Erling Smørgrav  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
21021e764dfSDag-Erling Smørgrav  * the right thing.
21121e764dfSDag-Erling Smørgrav  */
21221e764dfSDag-Erling Smørgrav #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
21321e764dfSDag-Erling Smørgrav static int
21421e764dfSDag-Erling Smørgrav sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
21521e764dfSDag-Erling Smørgrav {
21621e764dfSDag-Erling Smørgrav 	int result;
21721e764dfSDag-Erling Smørgrav 
21821e764dfSDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
21921e764dfSDag-Erling Smørgrav 		fatal("PAM: sshpam_authctxt not initialized");
22021e764dfSDag-Erling Smørgrav 	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
22121e764dfSDag-Erling Smørgrav 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
22221e764dfSDag-Erling Smørgrav 	result = pam_chauthtok(pamh, flags);
22321e764dfSDag-Erling Smørgrav 	if (setreuid(0, -1) == -1)
22421e764dfSDag-Erling Smørgrav 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
22521e764dfSDag-Erling Smørgrav 	return result;
22621e764dfSDag-Erling Smørgrav }
22721e764dfSDag-Erling Smørgrav # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
22821e764dfSDag-Erling Smørgrav #endif
22921e764dfSDag-Erling Smørgrav 
2301ec0d754SDag-Erling Smørgrav void
23121e764dfSDag-Erling Smørgrav sshpam_password_change_required(int reqd)
2321ec0d754SDag-Erling Smørgrav {
2331ec0d754SDag-Erling Smørgrav 	debug3("%s %d", __func__, reqd);
2345962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
2355962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
2365962c0e9SDag-Erling Smørgrav 	sshpam_authctxt->force_pwchange = reqd;
2371ec0d754SDag-Erling Smørgrav 	if (reqd) {
2381ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag |= 2;
2391ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag |= 2;
2401ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag |= 2;
2411ec0d754SDag-Erling Smørgrav 	} else {
2421ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag &= ~2;
2431ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag &= ~2;
2441ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag &= ~2;
2451ec0d754SDag-Erling Smørgrav 	}
2461ec0d754SDag-Erling Smørgrav }
2471ec0d754SDag-Erling Smørgrav 
2481ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */
2491ec0d754SDag-Erling Smørgrav static void
2501ec0d754SDag-Erling Smørgrav import_environments(Buffer *b)
2511ec0d754SDag-Erling Smørgrav {
2521ec0d754SDag-Erling Smørgrav 	char *env;
2531ec0d754SDag-Erling Smørgrav 	u_int i, num_env;
2541ec0d754SDag-Erling Smørgrav 	int err;
2551ec0d754SDag-Erling Smørgrav 
2561ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
2571ec0d754SDag-Erling Smørgrav 
2585962c0e9SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
2591ec0d754SDag-Erling Smørgrav 	/* Import variables set by do_pam_account */
2601ec0d754SDag-Erling Smørgrav 	sshpam_account_status = buffer_get_int(b);
26121e764dfSDag-Erling Smørgrav 	sshpam_password_change_required(buffer_get_int(b));
2621ec0d754SDag-Erling Smørgrav 
2631ec0d754SDag-Erling Smørgrav 	/* Import environment from subprocess */
2641ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
2651ec0d754SDag-Erling Smørgrav 	sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));
2661ec0d754SDag-Erling Smørgrav 	debug3("PAM: num env strings %d", num_env);
2671ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++)
2681ec0d754SDag-Erling Smørgrav 		sshpam_env[i] = buffer_get_string(b, NULL);
2691ec0d754SDag-Erling Smørgrav 
2701ec0d754SDag-Erling Smørgrav 	sshpam_env[num_env] = NULL;
2711ec0d754SDag-Erling Smørgrav 
2721ec0d754SDag-Erling Smørgrav 	/* Import PAM environment from subprocess */
2731ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
2741ec0d754SDag-Erling Smørgrav 	debug("PAM: num PAM env strings %d", num_env);
2751ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++) {
2761ec0d754SDag-Erling Smørgrav 		env = buffer_get_string(b, NULL);
2771ec0d754SDag-Erling Smørgrav 
2781ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
2791ec0d754SDag-Erling Smørgrav 		/* Errors are not fatal here */
2801ec0d754SDag-Erling Smørgrav 		if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
2811ec0d754SDag-Erling Smørgrav 			error("PAM: pam_putenv: %s",
2821ec0d754SDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
2831ec0d754SDag-Erling Smørgrav 		}
2841ec0d754SDag-Erling Smørgrav #endif
2851ec0d754SDag-Erling Smørgrav 	}
2865962c0e9SDag-Erling Smørgrav #endif
2871ec0d754SDag-Erling Smørgrav }
288cf2b5f3bSDag-Erling Smørgrav 
289cf2b5f3bSDag-Erling Smørgrav /*
290cf2b5f3bSDag-Erling Smørgrav  * Conversation function for authentication thread.
291cf2b5f3bSDag-Erling Smørgrav  */
292cf2b5f3bSDag-Erling Smørgrav static int
29321e764dfSDag-Erling Smørgrav sshpam_thread_conv(int n, struct pam_message **msg,
294cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
295cf2b5f3bSDag-Erling Smørgrav {
296cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
297cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
298cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
299cf2b5f3bSDag-Erling Smørgrav 	int i;
300cf2b5f3bSDag-Erling Smørgrav 
3011ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
302cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
303cf2b5f3bSDag-Erling Smørgrav 
30421e764dfSDag-Erling Smørgrav 	if (data == NULL) {
30521e764dfSDag-Erling Smørgrav 		error("PAM: conversation function passed a null context");
30621e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
30721e764dfSDag-Erling Smørgrav 	}
308cf2b5f3bSDag-Erling Smørgrav 	ctxt = data;
309cf2b5f3bSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
310cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
311cf2b5f3bSDag-Erling Smørgrav 
312cf2b5f3bSDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
313cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
314cf2b5f3bSDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
315cf2b5f3bSDag-Erling Smørgrav 
316cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
317cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
318cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
319cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
320cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
321cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3221ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3231ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3241ec0d754SDag-Erling Smørgrav 				goto fail;
3251ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
3261ec0d754SDag-Erling Smørgrav 				goto fail;
327cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
328cf2b5f3bSDag-Erling Smørgrav 				goto fail;
329cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
33009958426SBrian Feldman 			break;
331cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
332cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
333cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3341ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3351ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3361ec0d754SDag-Erling Smørgrav 				goto fail;
3371ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
3381ec0d754SDag-Erling Smørgrav 				goto fail;
339cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
340cf2b5f3bSDag-Erling Smørgrav 				goto fail;
341cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
342cf2b5f3bSDag-Erling Smørgrav 			break;
343cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
344cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
345cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3461ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3471ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3481ec0d754SDag-Erling Smørgrav 				goto fail;
349cf2b5f3bSDag-Erling Smørgrav 			break;
350cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
351cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
352cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3531ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3541ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3551ec0d754SDag-Erling Smørgrav 				goto fail;
356cf2b5f3bSDag-Erling Smørgrav 			break;
357cf2b5f3bSDag-Erling Smørgrav 		default:
358cf2b5f3bSDag-Erling Smørgrav 			goto fail;
359cf2b5f3bSDag-Erling Smørgrav 		}
360cf2b5f3bSDag-Erling Smørgrav 		buffer_clear(&buffer);
361cf2b5f3bSDag-Erling Smørgrav 	}
362cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
363cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
364cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
365cf2b5f3bSDag-Erling Smørgrav 
366cf2b5f3bSDag-Erling Smørgrav  fail:
367cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
368cf2b5f3bSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
369cf2b5f3bSDag-Erling Smørgrav 			xfree(reply[i].resp);
370cf2b5f3bSDag-Erling Smørgrav 	}
371cf2b5f3bSDag-Erling Smørgrav 	xfree(reply);
372cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
373cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
374cf2b5f3bSDag-Erling Smørgrav }
375cf2b5f3bSDag-Erling Smørgrav 
376cf2b5f3bSDag-Erling Smørgrav /*
377cf2b5f3bSDag-Erling Smørgrav  * Authentication thread.
378cf2b5f3bSDag-Erling Smørgrav  */
379cf2b5f3bSDag-Erling Smørgrav static void *
380cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp)
381cf2b5f3bSDag-Erling Smørgrav {
382cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
383cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
384cf2b5f3bSDag-Erling Smørgrav 	struct pam_conv sshpam_conv;
38521e764dfSDag-Erling Smørgrav 	int flags = (options.permit_empty_passwd == 0 ?
38621e764dfSDag-Erling Smørgrav 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
387cf2b5f3bSDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
3881ec0d754SDag-Erling Smørgrav 	extern char **environ;
3891ec0d754SDag-Erling Smørgrav 	char **env_from_pam;
3901ec0d754SDag-Erling Smørgrav 	u_int i;
391cf2b5f3bSDag-Erling Smørgrav 	const char *pam_user;
392cf2b5f3bSDag-Erling Smørgrav 
39321e764dfSDag-Erling Smørgrav 	pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user);
3941ec0d754SDag-Erling Smørgrav 	environ[0] = NULL;
39521e764dfSDag-Erling Smørgrav 
39621e764dfSDag-Erling Smørgrav 	if (sshpam_authctxt != NULL) {
39721e764dfSDag-Erling Smørgrav 		setproctitle("%s [pam]",
39821e764dfSDag-Erling Smørgrav 		    sshpam_authctxt->valid ? pam_user : "unknown");
39921e764dfSDag-Erling Smørgrav 	}
400cf2b5f3bSDag-Erling Smørgrav #endif
401cf2b5f3bSDag-Erling Smørgrav 
402cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.conv = sshpam_thread_conv;
403cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.appdata_ptr = ctxt;
404cf2b5f3bSDag-Erling Smørgrav 
4055962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
4065962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
4075962c0e9SDag-Erling Smørgrav 
408cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
409cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
410cf2b5f3bSDag-Erling Smørgrav 	    (const void *)&sshpam_conv);
411cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
412cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
41321e764dfSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, flags);
414cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
415cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
4161ec0d754SDag-Erling Smørgrav 
4171ec0d754SDag-Erling Smørgrav 	if (compat20) {
4181ec0d754SDag-Erling Smørgrav 		if (!do_pam_account())
4191ec0d754SDag-Erling Smørgrav 			goto auth_fail;
4205962c0e9SDag-Erling Smørgrav 		if (sshpam_authctxt->force_pwchange) {
4211ec0d754SDag-Erling Smørgrav 			sshpam_err = pam_chauthtok(sshpam_handle,
4221ec0d754SDag-Erling Smørgrav 			    PAM_CHANGE_EXPIRED_AUTHTOK);
4231ec0d754SDag-Erling Smørgrav 			if (sshpam_err != PAM_SUCCESS)
4241ec0d754SDag-Erling Smørgrav 				goto auth_fail;
42521e764dfSDag-Erling Smørgrav 			sshpam_password_change_required(0);
4261ec0d754SDag-Erling Smørgrav 		}
4271ec0d754SDag-Erling Smørgrav 	}
4281ec0d754SDag-Erling Smørgrav 
429cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer, "OK");
4301ec0d754SDag-Erling Smørgrav 
4311ec0d754SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
4321ec0d754SDag-Erling Smørgrav 	/* Export variables set by do_pam_account */
4331ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_account_status);
4345962c0e9SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
4351ec0d754SDag-Erling Smørgrav 
4361ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set in child */
4371ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
4381ec0d754SDag-Erling Smørgrav 		; /* Count */
4391ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
4401ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
4411ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, environ[i]);
4421ec0d754SDag-Erling Smørgrav 
4431ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set by PAM in child */
4441ec0d754SDag-Erling Smørgrav 	env_from_pam = pam_getenvlist(sshpam_handle);
4451ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
4461ec0d754SDag-Erling Smørgrav 		; /* Count */
4471ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
4481ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
4491ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, env_from_pam[i]);
4501ec0d754SDag-Erling Smørgrav #endif /* USE_POSIX_THREADS */
4511ec0d754SDag-Erling Smørgrav 
4521ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
453cf2b5f3bSDag-Erling Smørgrav 	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
454cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
455cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
456cf2b5f3bSDag-Erling Smørgrav 
457cf2b5f3bSDag-Erling Smørgrav  auth_fail:
458cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer,
459cf2b5f3bSDag-Erling Smørgrav 	    pam_strerror(sshpam_handle, sshpam_err));
4601ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
461cf2b5f3bSDag-Erling Smørgrav 	ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
462cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
463cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
464cf2b5f3bSDag-Erling Smørgrav 
465cf2b5f3bSDag-Erling Smørgrav 	return (NULL); /* Avoid warning for non-pthread case */
466cf2b5f3bSDag-Erling Smørgrav }
467cf2b5f3bSDag-Erling Smørgrav 
4681ec0d754SDag-Erling Smørgrav void
4691ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void)
470cf2b5f3bSDag-Erling Smørgrav {
4711ec0d754SDag-Erling Smørgrav 	struct pam_ctxt *ctxt = cleanup_ctxt;
472cf2b5f3bSDag-Erling Smørgrav 
4731ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
4741ec0d754SDag-Erling Smørgrav 	if (ctxt != NULL && ctxt->pam_thread != 0) {
475cf2b5f3bSDag-Erling Smørgrav 		pthread_cancel(ctxt->pam_thread);
476cf2b5f3bSDag-Erling Smørgrav 		pthread_join(ctxt->pam_thread, NULL);
477cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_psock);
478cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_csock);
4791ec0d754SDag-Erling Smørgrav 		memset(ctxt, 0, sizeof(*ctxt));
4801ec0d754SDag-Erling Smørgrav 		cleanup_ctxt = NULL;
4811ec0d754SDag-Erling Smørgrav 	}
482cf2b5f3bSDag-Erling Smørgrav }
483cf2b5f3bSDag-Erling Smørgrav 
484cf2b5f3bSDag-Erling Smørgrav static int
48521e764dfSDag-Erling Smørgrav sshpam_null_conv(int n, struct pam_message **msg,
486cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
487cf2b5f3bSDag-Erling Smørgrav {
4881ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
489cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
490cf2b5f3bSDag-Erling Smørgrav }
491cf2b5f3bSDag-Erling Smørgrav 
492cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL };
493cf2b5f3bSDag-Erling Smørgrav 
4941ec0d754SDag-Erling Smørgrav void
4951ec0d754SDag-Erling Smørgrav sshpam_cleanup(void)
496cf2b5f3bSDag-Erling Smørgrav {
497cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: cleanup");
498cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_handle == NULL)
499cf2b5f3bSDag-Erling Smørgrav 		return;
500cf2b5f3bSDag-Erling Smørgrav 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
501cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_cred_established) {
502cf2b5f3bSDag-Erling Smørgrav 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
503cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 0;
504cf2b5f3bSDag-Erling Smørgrav 	}
505cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_session_open) {
506cf2b5f3bSDag-Erling Smørgrav 		pam_close_session(sshpam_handle, PAM_SILENT);
507cf2b5f3bSDag-Erling Smørgrav 		sshpam_session_open = 0;
508cf2b5f3bSDag-Erling Smørgrav 	}
5091ec0d754SDag-Erling Smørgrav 	sshpam_authenticated = 0;
510cf2b5f3bSDag-Erling Smørgrav 	pam_end(sshpam_handle, sshpam_err);
511cf2b5f3bSDag-Erling Smørgrav 	sshpam_handle = NULL;
512cf2b5f3bSDag-Erling Smørgrav }
513cf2b5f3bSDag-Erling Smørgrav 
514cf2b5f3bSDag-Erling Smørgrav static int
5155962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt)
516cf2b5f3bSDag-Erling Smørgrav {
517cf2b5f3bSDag-Erling Smørgrav 	extern char *__progname;
5185962c0e9SDag-Erling Smørgrav 	const char *pam_rhost, *pam_user, *user = authctxt->user;
519cf2b5f3bSDag-Erling Smørgrav 
520cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_handle != NULL) {
521cf2b5f3bSDag-Erling Smørgrav 		/* We already have a PAM context; check if the user matches */
522cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_get_item(sshpam_handle,
52321e764dfSDag-Erling Smørgrav 		    PAM_USER, (void **)&pam_user);
524cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
525cf2b5f3bSDag-Erling Smørgrav 			return (0);
526cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
527cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
528cf2b5f3bSDag-Erling Smørgrav 	}
529cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: initializing for \"%s\"", user);
530cf2b5f3bSDag-Erling Smørgrav 	sshpam_err =
531cf2b5f3bSDag-Erling Smørgrav 	    pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
5325962c0e9SDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
5335962c0e9SDag-Erling Smørgrav 
534cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
535cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
536cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
537cf2b5f3bSDag-Erling Smørgrav 		return (-1);
538cf2b5f3bSDag-Erling Smørgrav 	}
539cf2b5f3bSDag-Erling Smørgrav 	pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
540cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
541cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
542cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
543cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
544cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
545cf2b5f3bSDag-Erling Smørgrav 		return (-1);
546cf2b5f3bSDag-Erling Smørgrav 	}
547cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE
548cf2b5f3bSDag-Erling Smørgrav 	/*
549cf2b5f3bSDag-Erling Smørgrav 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
550cf2b5f3bSDag-Erling Smørgrav 	 * sshd doesn't set the tty until too late in the auth process and
551cf2b5f3bSDag-Erling Smørgrav 	 * may not even set one (for tty-less connections)
552cf2b5f3bSDag-Erling Smørgrav 	 */
553cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_TTY to \"ssh\"");
554cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
555cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
556cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
557cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
558cf2b5f3bSDag-Erling Smørgrav 		return (-1);
559cf2b5f3bSDag-Erling Smørgrav 	}
560cf2b5f3bSDag-Erling Smørgrav #endif
561cf2b5f3bSDag-Erling Smørgrav 	return (0);
562cf2b5f3bSDag-Erling Smørgrav }
563cf2b5f3bSDag-Erling Smørgrav 
564cf2b5f3bSDag-Erling Smørgrav static void *
565cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt)
566cf2b5f3bSDag-Erling Smørgrav {
567cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
568cf2b5f3bSDag-Erling Smørgrav 	int socks[2];
569cf2b5f3bSDag-Erling Smørgrav 
5701ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
571cf2b5f3bSDag-Erling Smørgrav 	/* Refuse to start if we don't have PAM enabled */
572cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
573cf2b5f3bSDag-Erling Smørgrav 		return NULL;
574cf2b5f3bSDag-Erling Smørgrav 
575cf2b5f3bSDag-Erling Smørgrav 	/* Initialize PAM */
5765962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1) {
577cf2b5f3bSDag-Erling Smørgrav 		error("PAM: initialization failed");
578cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
579cf2b5f3bSDag-Erling Smørgrav 	}
580cf2b5f3bSDag-Erling Smørgrav 
581cf2b5f3bSDag-Erling Smørgrav 	ctxt = xmalloc(sizeof *ctxt);
5821ec0d754SDag-Erling Smørgrav 	memset(ctxt, 0, sizeof(*ctxt));
5831ec0d754SDag-Erling Smørgrav 
584cf2b5f3bSDag-Erling Smørgrav 	/* Start the authentication thread */
585cf2b5f3bSDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
586cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed create sockets: %s", strerror(errno));
587cf2b5f3bSDag-Erling Smørgrav 		xfree(ctxt);
588cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
589cf2b5f3bSDag-Erling Smørgrav 	}
590cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_psock = socks[0];
591cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_csock = socks[1];
592cf2b5f3bSDag-Erling Smørgrav 	if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
593cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed to start authentication thread: %s",
594cf2b5f3bSDag-Erling Smørgrav 		    strerror(errno));
595cf2b5f3bSDag-Erling Smørgrav 		close(socks[0]);
596cf2b5f3bSDag-Erling Smørgrav 		close(socks[1]);
597cf2b5f3bSDag-Erling Smørgrav 		xfree(ctxt);
598cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
599cf2b5f3bSDag-Erling Smørgrav 	}
6001ec0d754SDag-Erling Smørgrav 	cleanup_ctxt = ctxt;
601cf2b5f3bSDag-Erling Smørgrav 	return (ctxt);
602cf2b5f3bSDag-Erling Smørgrav }
603cf2b5f3bSDag-Erling Smørgrav 
604cf2b5f3bSDag-Erling Smørgrav static int
605cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info,
606cf2b5f3bSDag-Erling Smørgrav     u_int *num, char ***prompts, u_int **echo_on)
607cf2b5f3bSDag-Erling Smørgrav {
608cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
609cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
610cf2b5f3bSDag-Erling Smørgrav 	size_t plen;
611cf2b5f3bSDag-Erling Smørgrav 	u_char type;
612cf2b5f3bSDag-Erling Smørgrav 	char *msg;
613cf2b5f3bSDag-Erling Smørgrav 	size_t len;
614cf2b5f3bSDag-Erling Smørgrav 
6151ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
616cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
617cf2b5f3bSDag-Erling Smørgrav 	*name = xstrdup("");
618cf2b5f3bSDag-Erling Smørgrav 	*info = xstrdup("");
619cf2b5f3bSDag-Erling Smørgrav 	*prompts = xmalloc(sizeof(char *));
620cf2b5f3bSDag-Erling Smørgrav 	**prompts = NULL;
621cf2b5f3bSDag-Erling Smørgrav 	plen = 0;
622cf2b5f3bSDag-Erling Smørgrav 	*echo_on = xmalloc(sizeof(u_int));
623cf2b5f3bSDag-Erling Smørgrav 	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
624cf2b5f3bSDag-Erling Smørgrav 		type = buffer_get_char(&buffer);
625cf2b5f3bSDag-Erling Smørgrav 		msg = buffer_get_string(&buffer, NULL);
626cf2b5f3bSDag-Erling Smørgrav 		switch (type) {
627cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
628cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
629cf2b5f3bSDag-Erling Smørgrav 			*num = 1;
630cf2b5f3bSDag-Erling Smørgrav 			len = plen + strlen(msg) + 1;
631cf2b5f3bSDag-Erling Smørgrav 			**prompts = xrealloc(**prompts, len);
632cf2b5f3bSDag-Erling Smørgrav 			plen += snprintf(**prompts + plen, len, "%s", msg);
633cf2b5f3bSDag-Erling Smørgrav 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
634cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
635cf2b5f3bSDag-Erling Smørgrav 			return (0);
636cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
637cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
638cf2b5f3bSDag-Erling Smørgrav 			/* accumulate messages */
6391ec0d754SDag-Erling Smørgrav 			len = plen + strlen(msg) + 2;
640cf2b5f3bSDag-Erling Smørgrav 			**prompts = xrealloc(**prompts, len);
6411ec0d754SDag-Erling Smørgrav 			plen += snprintf(**prompts + plen, len, "%s\n", msg);
642cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
643cf2b5f3bSDag-Erling Smørgrav 			break;
644cf2b5f3bSDag-Erling Smørgrav 		case PAM_SUCCESS:
645cf2b5f3bSDag-Erling Smørgrav 		case PAM_AUTH_ERR:
646cf2b5f3bSDag-Erling Smørgrav 			if (**prompts != NULL) {
647cf2b5f3bSDag-Erling Smørgrav 				/* drain any accumulated messages */
6481ec0d754SDag-Erling Smørgrav 				debug("PAM: %s", **prompts);
6491ec0d754SDag-Erling Smørgrav 				buffer_append(&loginmsg, **prompts,
6501ec0d754SDag-Erling Smørgrav 				    strlen(**prompts));
651cf2b5f3bSDag-Erling Smørgrav 				xfree(**prompts);
652cf2b5f3bSDag-Erling Smørgrav 				**prompts = NULL;
653cf2b5f3bSDag-Erling Smørgrav 			}
654cf2b5f3bSDag-Erling Smørgrav 			if (type == PAM_SUCCESS) {
6551ec0d754SDag-Erling Smørgrav 				import_environments(&buffer);
656cf2b5f3bSDag-Erling Smørgrav 				*num = 0;
657cf2b5f3bSDag-Erling Smørgrav 				**echo_on = 0;
658cf2b5f3bSDag-Erling Smørgrav 				ctxt->pam_done = 1;
659cf2b5f3bSDag-Erling Smørgrav 				xfree(msg);
660cf2b5f3bSDag-Erling Smørgrav 				return (0);
661cf2b5f3bSDag-Erling Smørgrav 			}
6625962c0e9SDag-Erling Smørgrav 			error("PAM: %s for %s%.100s from %.100s", msg,
6635962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->valid ? "" : "illegal user ",
6645962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->user,
6655962c0e9SDag-Erling Smørgrav 			    get_remote_name_or_ip(utmp_len, options.use_dns));
6661ec0d754SDag-Erling Smørgrav 			/* FALLTHROUGH */
667cf2b5f3bSDag-Erling Smørgrav 		default:
668cf2b5f3bSDag-Erling Smørgrav 			*num = 0;
669cf2b5f3bSDag-Erling Smørgrav 			**echo_on = 0;
670cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
671cf2b5f3bSDag-Erling Smørgrav 			ctxt->pam_done = -1;
672cf2b5f3bSDag-Erling Smørgrav 			return (-1);
673cf2b5f3bSDag-Erling Smørgrav 		}
674cf2b5f3bSDag-Erling Smørgrav 	}
675cf2b5f3bSDag-Erling Smørgrav 	return (-1);
676cf2b5f3bSDag-Erling Smørgrav }
677cf2b5f3bSDag-Erling Smørgrav 
678cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */
679cf2b5f3bSDag-Erling Smørgrav static int
680cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp)
681cf2b5f3bSDag-Erling Smørgrav {
682cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
683cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
684cf2b5f3bSDag-Erling Smørgrav 
6851ec0d754SDag-Erling Smørgrav 	debug2("PAM: %s entering, %d responses", __func__, num);
686cf2b5f3bSDag-Erling Smørgrav 	switch (ctxt->pam_done) {
687cf2b5f3bSDag-Erling Smørgrav 	case 1:
688cf2b5f3bSDag-Erling Smørgrav 		sshpam_authenticated = 1;
689cf2b5f3bSDag-Erling Smørgrav 		return (0);
690cf2b5f3bSDag-Erling Smørgrav 	case 0:
691cf2b5f3bSDag-Erling Smørgrav 		break;
692cf2b5f3bSDag-Erling Smørgrav 	default:
693cf2b5f3bSDag-Erling Smørgrav 		return (-1);
694cf2b5f3bSDag-Erling Smørgrav 	}
695cf2b5f3bSDag-Erling Smørgrav 	if (num != 1) {
696cf2b5f3bSDag-Erling Smørgrav 		error("PAM: expected one response, got %u", num);
697cf2b5f3bSDag-Erling Smørgrav 		return (-1);
698cf2b5f3bSDag-Erling Smørgrav 	}
699cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
700cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer, *resp);
7011ec0d754SDag-Erling Smørgrav 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
7021ec0d754SDag-Erling Smørgrav 		buffer_free(&buffer);
7031ec0d754SDag-Erling Smørgrav 		return (-1);
7041ec0d754SDag-Erling Smørgrav 	}
705cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
706cf2b5f3bSDag-Erling Smørgrav 	return (1);
707cf2b5f3bSDag-Erling Smørgrav }
708cf2b5f3bSDag-Erling Smørgrav 
709cf2b5f3bSDag-Erling Smørgrav static void
710cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp)
711cf2b5f3bSDag-Erling Smørgrav {
712cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
713cf2b5f3bSDag-Erling Smørgrav 
7141ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
7151ec0d754SDag-Erling Smørgrav 	sshpam_thread_cleanup();
716cf2b5f3bSDag-Erling Smørgrav 	xfree(ctxt);
717cf2b5f3bSDag-Erling Smørgrav 	/*
718cf2b5f3bSDag-Erling Smørgrav 	 * We don't call sshpam_cleanup() here because we may need the PAM
719cf2b5f3bSDag-Erling Smørgrav 	 * handle at a later stage, e.g. when setting up a session.  It's
720cf2b5f3bSDag-Erling Smørgrav 	 * still on the cleanup list, so pam_end() *will* be called before
721cf2b5f3bSDag-Erling Smørgrav 	 * the server process terminates.
722cf2b5f3bSDag-Erling Smørgrav 	 */
723cf2b5f3bSDag-Erling Smørgrav }
724cf2b5f3bSDag-Erling Smørgrav 
725cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = {
726cf2b5f3bSDag-Erling Smørgrav 	"pam",
727cf2b5f3bSDag-Erling Smørgrav 	sshpam_init_ctx,
728cf2b5f3bSDag-Erling Smørgrav 	sshpam_query,
729cf2b5f3bSDag-Erling Smørgrav 	sshpam_respond,
730cf2b5f3bSDag-Erling Smørgrav 	sshpam_free_ctx
731cf2b5f3bSDag-Erling Smørgrav };
732cf2b5f3bSDag-Erling Smørgrav 
733cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = {
734cf2b5f3bSDag-Erling Smørgrav 	"pam",
735cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_init_ctx,
736cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_query,
737cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_respond,
738cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_free_ctx
739cf2b5f3bSDag-Erling Smørgrav };
740cf2b5f3bSDag-Erling Smørgrav 
741cf2b5f3bSDag-Erling Smørgrav /*
742cf2b5f3bSDag-Erling Smørgrav  * This replaces auth-pam.c
743cf2b5f3bSDag-Erling Smørgrav  */
744cf2b5f3bSDag-Erling Smørgrav void
7455962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt)
746cf2b5f3bSDag-Erling Smørgrav {
747cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
748cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation requested when UsePAM=no");
749cf2b5f3bSDag-Erling Smørgrav 
7505962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1)
751cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation failed");
752cf2b5f3bSDag-Erling Smørgrav }
753cf2b5f3bSDag-Erling Smørgrav 
754cf2b5f3bSDag-Erling Smørgrav void
755cf2b5f3bSDag-Erling Smørgrav finish_pam(void)
756cf2b5f3bSDag-Erling Smørgrav {
7571ec0d754SDag-Erling Smørgrav 	sshpam_cleanup();
758cf2b5f3bSDag-Erling Smørgrav }
759cf2b5f3bSDag-Erling Smørgrav 
760cf2b5f3bSDag-Erling Smørgrav u_int
761cf2b5f3bSDag-Erling Smørgrav do_pam_account(void)
762cf2b5f3bSDag-Erling Smørgrav {
7631ec0d754SDag-Erling Smørgrav 	if (sshpam_account_status != -1)
7641ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
7651ec0d754SDag-Erling Smørgrav 
766cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
7671ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s pam_acct_mgmt = %d", __func__, sshpam_err);
768cf2b5f3bSDag-Erling Smørgrav 
7691ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
7701ec0d754SDag-Erling Smørgrav 		sshpam_account_status = 0;
7711ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
77209958426SBrian Feldman 	}
77309958426SBrian Feldman 
7741ec0d754SDag-Erling Smørgrav 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
77521e764dfSDag-Erling Smørgrav 		sshpam_password_change_required(1);
77609958426SBrian Feldman 
7771ec0d754SDag-Erling Smørgrav 	sshpam_account_status = 1;
7781ec0d754SDag-Erling Smørgrav 	return (sshpam_account_status);
77909958426SBrian Feldman }
78009958426SBrian Feldman 
781cf2b5f3bSDag-Erling Smørgrav void
782cf2b5f3bSDag-Erling Smørgrav do_pam_set_tty(const char *tty)
783cf2b5f3bSDag-Erling Smørgrav {
784cf2b5f3bSDag-Erling Smørgrav 	if (tty != NULL) {
785cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: setting PAM_TTY to \"%s\"", tty);
786cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
787cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err != PAM_SUCCESS)
788cf2b5f3bSDag-Erling Smørgrav 			fatal("PAM: failed to set PAM_TTY: %s",
789cf2b5f3bSDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
790cf2b5f3bSDag-Erling Smørgrav 	}
79109958426SBrian Feldman }
79209958426SBrian Feldman 
793cf2b5f3bSDag-Erling Smørgrav void
794cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init)
79509958426SBrian Feldman {
796cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
797cf2b5f3bSDag-Erling Smørgrav 	    (const void *)&null_conv);
798cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
799cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
800cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
801cf2b5f3bSDag-Erling Smørgrav 	if (init) {
802cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: establishing credentials");
803cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
804cf2b5f3bSDag-Erling Smørgrav 	} else {
805cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: reinitializing credentials");
806cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
807cf2b5f3bSDag-Erling Smørgrav 	}
808cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS) {
809cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 1;
810989dd127SDag-Erling Smørgrav 		return;
811cf2b5f3bSDag-Erling Smørgrav 	}
812cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_authenticated)
813cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_setcred(): %s",
814cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
8152c917d39SAlfred Perlstein 	else
816cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: pam_setcred(): %s",
817cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
81809958426SBrian Feldman }
81909958426SBrian Feldman 
820cf2b5f3bSDag-Erling Smørgrav static int
82121e764dfSDag-Erling Smørgrav sshpam_tty_conv(int n, struct pam_message **msg,
822cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
82309958426SBrian Feldman {
824cf2b5f3bSDag-Erling Smørgrav 	char input[PAM_MAX_MSG_SIZE];
825cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
826f388f5efSDag-Erling Smørgrav 	int i;
827f388f5efSDag-Erling Smørgrav 
8281ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
8291ec0d754SDag-Erling Smørgrav 
830cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
831cf2b5f3bSDag-Erling Smørgrav 
8321ec0d754SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
833cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
834cf2b5f3bSDag-Erling Smørgrav 
835cf2b5f3bSDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
836cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
837cf2b5f3bSDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
838cf2b5f3bSDag-Erling Smørgrav 
839cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
840cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
841cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
842cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp =
843cf2b5f3bSDag-Erling Smørgrav 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
844cf2b5f3bSDag-Erling Smørgrav 			    RP_ALLOW_STDIN);
845cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
846cf2b5f3bSDag-Erling Smørgrav 			break;
847cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
8481ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
849cf2b5f3bSDag-Erling Smørgrav 			fgets(input, sizeof input, stdin);
85021e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup(input)) == NULL)
85121e764dfSDag-Erling Smørgrav 				goto fail;
852cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
853cf2b5f3bSDag-Erling Smørgrav 			break;
854cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
855cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
8561ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
857cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
858cf2b5f3bSDag-Erling Smørgrav 			break;
859cf2b5f3bSDag-Erling Smørgrav 		default:
860cf2b5f3bSDag-Erling Smørgrav 			goto fail;
861f388f5efSDag-Erling Smørgrav 		}
862f388f5efSDag-Erling Smørgrav 	}
863cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
864cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
865cf2b5f3bSDag-Erling Smørgrav 
866cf2b5f3bSDag-Erling Smørgrav  fail:
867cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
868cf2b5f3bSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
869cf2b5f3bSDag-Erling Smørgrav 			xfree(reply[i].resp);
870cf2b5f3bSDag-Erling Smørgrav 	}
871cf2b5f3bSDag-Erling Smørgrav 	xfree(reply);
872cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
873cf2b5f3bSDag-Erling Smørgrav }
874f388f5efSDag-Erling Smørgrav 
87521e764dfSDag-Erling Smørgrav static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
8761ec0d754SDag-Erling Smørgrav 
877cf2b5f3bSDag-Erling Smørgrav /*
878cf2b5f3bSDag-Erling Smørgrav  * XXX this should be done in the authentication phase, but ssh1 doesn't
879cf2b5f3bSDag-Erling Smørgrav  * support that
880cf2b5f3bSDag-Erling Smørgrav  */
881cf2b5f3bSDag-Erling Smørgrav void
882cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void)
88309958426SBrian Feldman {
884cf2b5f3bSDag-Erling Smørgrav 	if (use_privsep)
885cf2b5f3bSDag-Erling Smørgrav 		fatal("Password expired (unable to change with privsep)");
886cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
8871ec0d754SDag-Erling Smørgrav 	    (const void *)&tty_conv);
888cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
889cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
890cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
891cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: changing password");
892cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
893cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
894cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_chauthtok(): %s",
895cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
89609958426SBrian Feldman }
89709958426SBrian Feldman 
8981ec0d754SDag-Erling Smørgrav static int
89921e764dfSDag-Erling Smørgrav sshpam_store_conv(int n, struct pam_message **msg,
9001ec0d754SDag-Erling Smørgrav     struct pam_response **resp, void *data)
9011ec0d754SDag-Erling Smørgrav {
9021ec0d754SDag-Erling Smørgrav 	struct pam_response *reply;
9031ec0d754SDag-Erling Smørgrav 	int i;
9041ec0d754SDag-Erling Smørgrav 	size_t len;
9051ec0d754SDag-Erling Smørgrav 
9061ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
9071ec0d754SDag-Erling Smørgrav 	*resp = NULL;
9081ec0d754SDag-Erling Smørgrav 
9091ec0d754SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
9101ec0d754SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
9111ec0d754SDag-Erling Smørgrav 
9121ec0d754SDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
9131ec0d754SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
9141ec0d754SDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
9151ec0d754SDag-Erling Smørgrav 
9161ec0d754SDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
9171ec0d754SDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
9181ec0d754SDag-Erling Smørgrav 		case PAM_ERROR_MSG:
9191ec0d754SDag-Erling Smørgrav 		case PAM_TEXT_INFO:
9201ec0d754SDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
9211ec0d754SDag-Erling Smørgrav 			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
9221ec0d754SDag-Erling Smørgrav 			buffer_append(&loginmsg, "\n", 1 );
9231ec0d754SDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
9241ec0d754SDag-Erling Smørgrav 			break;
9251ec0d754SDag-Erling Smørgrav 		default:
9261ec0d754SDag-Erling Smørgrav 			goto fail;
9271ec0d754SDag-Erling Smørgrav 		}
9281ec0d754SDag-Erling Smørgrav 	}
9291ec0d754SDag-Erling Smørgrav 	*resp = reply;
9301ec0d754SDag-Erling Smørgrav 	return (PAM_SUCCESS);
9311ec0d754SDag-Erling Smørgrav 
9321ec0d754SDag-Erling Smørgrav  fail:
9331ec0d754SDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
9341ec0d754SDag-Erling Smørgrav 		if (reply[i].resp != NULL)
9351ec0d754SDag-Erling Smørgrav 			xfree(reply[i].resp);
9361ec0d754SDag-Erling Smørgrav 	}
9371ec0d754SDag-Erling Smørgrav 	xfree(reply);
9381ec0d754SDag-Erling Smørgrav 	return (PAM_CONV_ERR);
9391ec0d754SDag-Erling Smørgrav }
9401ec0d754SDag-Erling Smørgrav 
94121e764dfSDag-Erling Smørgrav static struct pam_conv store_conv = { sshpam_store_conv, NULL };
9421ec0d754SDag-Erling Smørgrav 
9431ec0d754SDag-Erling Smørgrav void
9441ec0d754SDag-Erling Smørgrav do_pam_session(void)
9451ec0d754SDag-Erling Smørgrav {
9461ec0d754SDag-Erling Smørgrav 	debug3("PAM: opening session");
9471ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
9481ec0d754SDag-Erling Smørgrav 	    (const void *)&store_conv);
9491ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
9501ec0d754SDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
9511ec0d754SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
9521ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_open_session(sshpam_handle, 0);
9531ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
9541ec0d754SDag-Erling Smørgrav 		fatal("PAM: pam_open_session(): %s",
9551ec0d754SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
9561ec0d754SDag-Erling Smørgrav 	sshpam_session_open = 1;
9571ec0d754SDag-Erling Smørgrav }
9581ec0d754SDag-Erling Smørgrav 
959cf2b5f3bSDag-Erling Smørgrav /*
960cf2b5f3bSDag-Erling Smørgrav  * Set a PAM environment string. We need to do this so that the session
961cf2b5f3bSDag-Erling Smørgrav  * modules can handle things like Kerberos/GSI credentials that appear
962cf2b5f3bSDag-Erling Smørgrav  * during the ssh authentication process.
963cf2b5f3bSDag-Erling Smørgrav  */
964cf2b5f3bSDag-Erling Smørgrav int
965cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value)
96609958426SBrian Feldman {
967cf2b5f3bSDag-Erling Smørgrav 	int ret = 1;
968cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
969cf2b5f3bSDag-Erling Smørgrav 	char *compound;
970cf2b5f3bSDag-Erling Smørgrav 	size_t len;
97109958426SBrian Feldman 
972cf2b5f3bSDag-Erling Smørgrav 	len = strlen(name) + strlen(value) + 2;
973cf2b5f3bSDag-Erling Smørgrav 	compound = xmalloc(len);
97409958426SBrian Feldman 
975cf2b5f3bSDag-Erling Smørgrav 	snprintf(compound, len, "%s=%s", name, value);
976cf2b5f3bSDag-Erling Smørgrav 	ret = pam_putenv(sshpam_handle, compound);
977cf2b5f3bSDag-Erling Smørgrav 	xfree(compound);
978cf2b5f3bSDag-Erling Smørgrav #endif
97909958426SBrian Feldman 
980cf2b5f3bSDag-Erling Smørgrav 	return (ret);
981cf2b5f3bSDag-Erling Smørgrav }
98209958426SBrian Feldman 
9831ec0d754SDag-Erling Smørgrav char **
9841ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void)
985cf2b5f3bSDag-Erling Smørgrav {
9861ec0d754SDag-Erling Smørgrav 	return sshpam_env;
987cf2b5f3bSDag-Erling Smørgrav }
988cf2b5f3bSDag-Erling Smørgrav 
989cf2b5f3bSDag-Erling Smørgrav char **
990cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void)
991cf2b5f3bSDag-Erling Smørgrav {
992cf2b5f3bSDag-Erling Smørgrav 	return (pam_getenvlist(sshpam_handle));
993cf2b5f3bSDag-Erling Smørgrav }
994cf2b5f3bSDag-Erling Smørgrav 
995cf2b5f3bSDag-Erling Smørgrav void
996cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env)
997cf2b5f3bSDag-Erling Smørgrav {
998cf2b5f3bSDag-Erling Smørgrav 	char **envp;
999cf2b5f3bSDag-Erling Smørgrav 
1000cf2b5f3bSDag-Erling Smørgrav 	if (env == NULL)
1001cf2b5f3bSDag-Erling Smørgrav 		return;
1002cf2b5f3bSDag-Erling Smørgrav 
1003cf2b5f3bSDag-Erling Smørgrav 	for (envp = env; *envp; envp++)
1004cf2b5f3bSDag-Erling Smørgrav 		xfree(*envp);
1005cf2b5f3bSDag-Erling Smørgrav 	xfree(env);
100609958426SBrian Feldman }
100709958426SBrian Feldman 
100821e764dfSDag-Erling Smørgrav /*
100921e764dfSDag-Erling Smørgrav  * "Blind" conversation function for password authentication.  Assumes that
101021e764dfSDag-Erling Smørgrav  * echo-off prompts are for the password and stores messages for later
101121e764dfSDag-Erling Smørgrav  * display.
101221e764dfSDag-Erling Smørgrav  */
101321e764dfSDag-Erling Smørgrav static int
101421e764dfSDag-Erling Smørgrav sshpam_passwd_conv(int n, struct pam_message **msg,
101521e764dfSDag-Erling Smørgrav     struct pam_response **resp, void *data)
101621e764dfSDag-Erling Smørgrav {
101721e764dfSDag-Erling Smørgrav 	struct pam_response *reply;
101821e764dfSDag-Erling Smørgrav 	int i;
101921e764dfSDag-Erling Smørgrav 	size_t len;
102021e764dfSDag-Erling Smørgrav 
102121e764dfSDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
102221e764dfSDag-Erling Smørgrav 
102321e764dfSDag-Erling Smørgrav 	*resp = NULL;
102421e764dfSDag-Erling Smørgrav 
102521e764dfSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
102621e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
102721e764dfSDag-Erling Smørgrav 
102821e764dfSDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
102921e764dfSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
103021e764dfSDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
103121e764dfSDag-Erling Smørgrav 
103221e764dfSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
103321e764dfSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
103421e764dfSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
103521e764dfSDag-Erling Smørgrav 			if (sshpam_password == NULL)
103621e764dfSDag-Erling Smørgrav 				goto fail;
103721e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
103821e764dfSDag-Erling Smørgrav 				goto fail;
103921e764dfSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
104021e764dfSDag-Erling Smørgrav 			break;
104121e764dfSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
104221e764dfSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
104321e764dfSDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
104421e764dfSDag-Erling Smørgrav 			if (len > 0) {
104521e764dfSDag-Erling Smørgrav 				buffer_append(&loginmsg,
104621e764dfSDag-Erling Smørgrav 				    PAM_MSG_MEMBER(msg, i, msg), len);
104721e764dfSDag-Erling Smørgrav 				buffer_append(&loginmsg, "\n", 1);
104821e764dfSDag-Erling Smørgrav 			}
104921e764dfSDag-Erling Smørgrav 			if ((reply[i].resp = strdup("")) == NULL)
105021e764dfSDag-Erling Smørgrav 				goto fail;
105121e764dfSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
105221e764dfSDag-Erling Smørgrav 			break;
105321e764dfSDag-Erling Smørgrav 		default:
105421e764dfSDag-Erling Smørgrav 			goto fail;
105521e764dfSDag-Erling Smørgrav 		}
105621e764dfSDag-Erling Smørgrav 	}
105721e764dfSDag-Erling Smørgrav 	*resp = reply;
105821e764dfSDag-Erling Smørgrav 	return (PAM_SUCCESS);
105921e764dfSDag-Erling Smørgrav 
106021e764dfSDag-Erling Smørgrav  fail:
106121e764dfSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
106221e764dfSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
106321e764dfSDag-Erling Smørgrav 			xfree(reply[i].resp);
106421e764dfSDag-Erling Smørgrav 	}
106521e764dfSDag-Erling Smørgrav 	xfree(reply);
106621e764dfSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
106721e764dfSDag-Erling Smørgrav }
106821e764dfSDag-Erling Smørgrav 
106921e764dfSDag-Erling Smørgrav static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
107021e764dfSDag-Erling Smørgrav 
107121e764dfSDag-Erling Smørgrav /*
107221e764dfSDag-Erling Smørgrav  * Attempt password authentication via PAM
107321e764dfSDag-Erling Smørgrav  */
107421e764dfSDag-Erling Smørgrav int
107521e764dfSDag-Erling Smørgrav sshpam_auth_passwd(Authctxt *authctxt, const char *password)
107621e764dfSDag-Erling Smørgrav {
107721e764dfSDag-Erling Smørgrav 	int flags = (options.permit_empty_passwd == 0 ?
107821e764dfSDag-Erling Smørgrav 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
107921e764dfSDag-Erling Smørgrav 	static char badpw[] = "\b\n\r\177INCORRECT";
108021e764dfSDag-Erling Smørgrav 
108121e764dfSDag-Erling Smørgrav 	if (!options.use_pam || sshpam_handle == NULL)
108221e764dfSDag-Erling Smørgrav 		fatal("PAM: %s called when PAM disabled or failed to "
108321e764dfSDag-Erling Smørgrav 		    "initialise.", __func__);
108421e764dfSDag-Erling Smørgrav 
108521e764dfSDag-Erling Smørgrav 	sshpam_password = password;
108621e764dfSDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
108721e764dfSDag-Erling Smørgrav 
108821e764dfSDag-Erling Smørgrav 	/*
108921e764dfSDag-Erling Smørgrav 	 * If the user logging in is invalid, or is root but is not permitted
109021e764dfSDag-Erling Smørgrav 	 * by PermitRootLogin, use an invalid password to prevent leaking
109121e764dfSDag-Erling Smørgrav 	 * information via timing (eg if the PAM config has a delay on fail).
109221e764dfSDag-Erling Smørgrav 	 */
109321e764dfSDag-Erling Smørgrav 	if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
109421e764dfSDag-Erling Smørgrav 	     options.permit_root_login != PERMIT_YES))
109521e764dfSDag-Erling Smørgrav 		sshpam_password = badpw;
109621e764dfSDag-Erling Smørgrav 
109721e764dfSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
109821e764dfSDag-Erling Smørgrav 	    (const void *)&passwd_conv);
109921e764dfSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
110021e764dfSDag-Erling Smørgrav 		fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
110121e764dfSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
110221e764dfSDag-Erling Smørgrav 
110321e764dfSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, flags);
110421e764dfSDag-Erling Smørgrav 	sshpam_password = NULL;
110521e764dfSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
110621e764dfSDag-Erling Smørgrav 		debug("PAM: password authentication accepted for %.100s",
110721e764dfSDag-Erling Smørgrav 		    authctxt->user);
110821e764dfSDag-Erling Smørgrav                return 1;
110921e764dfSDag-Erling Smørgrav 	} else {
111021e764dfSDag-Erling Smørgrav 		debug("PAM: password authentication failed for %.100s: %s",
111121e764dfSDag-Erling Smørgrav 		    authctxt->valid ? authctxt->user : "an illegal user",
111221e764dfSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
111321e764dfSDag-Erling Smørgrav 		return 0;
111421e764dfSDag-Erling Smørgrav 	}
111521e764dfSDag-Erling Smørgrav }
111609958426SBrian Feldman #endif /* USE_PAM */
1117