xref: /freebsd/crypto/openssh/auth-pam.c (revision 5962c0e9a3a89f39087a04044502f9b3a66676e6)
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  */
3109958426SBrian Feldman 
32cf2b5f3bSDag-Erling Smørgrav /* Based on $xFreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
3309958426SBrian Feldman #include "includes.h"
345962c0e9SDag-Erling Smørgrav RCSID("$Id: auth-pam.c,v 1.100 2004/04/18 01:00:26 dtucker Exp $");
35cf2b5f3bSDag-Erling Smørgrav RCSID("$FreeBSD$");
3609958426SBrian Feldman 
3709958426SBrian Feldman #ifdef USE_PAM
381ec0d754SDag-Erling Smørgrav #if defined(HAVE_SECURITY_PAM_APPL_H)
39cf2b5f3bSDag-Erling Smørgrav #include <security/pam_appl.h>
401ec0d754SDag-Erling Smørgrav #elif defined (HAVE_PAM_PAM_APPL_H)
411ec0d754SDag-Erling Smørgrav #include <pam/pam_appl.h>
421ec0d754SDag-Erling Smørgrav #endif
43cf2b5f3bSDag-Erling Smørgrav 
44989dd127SDag-Erling Smørgrav #include "auth.h"
45989dd127SDag-Erling Smørgrav #include "auth-pam.h"
46cf2b5f3bSDag-Erling Smørgrav #include "buffer.h"
47cf2b5f3bSDag-Erling Smørgrav #include "bufaux.h"
484c5de869SBrian Feldman #include "canohost.h"
49cf2b5f3bSDag-Erling Smørgrav #include "log.h"
50cf2b5f3bSDag-Erling Smørgrav #include "monitor_wrap.h"
51cf2b5f3bSDag-Erling Smørgrav #include "msg.h"
52cf2b5f3bSDag-Erling Smørgrav #include "packet.h"
53989dd127SDag-Erling Smørgrav #include "readpass.h"
54cf2b5f3bSDag-Erling Smørgrav #include "servconf.h"
55cf2b5f3bSDag-Erling Smørgrav #include "ssh2.h"
56cf2b5f3bSDag-Erling Smørgrav #include "xmalloc.h"
57cf2b5f3bSDag-Erling Smørgrav #include "auth-options.h"
5809958426SBrian Feldman 
59989dd127SDag-Erling Smørgrav extern ServerOptions options;
601ec0d754SDag-Erling Smørgrav extern Buffer loginmsg;
611ec0d754SDag-Erling Smørgrav extern int compat20;
625962c0e9SDag-Erling Smørgrav extern u_int utmp_len;
632c917d39SAlfred Perlstein 
64cf2b5f3bSDag-Erling Smørgrav #ifdef USE_POSIX_THREADS
65cf2b5f3bSDag-Erling Smørgrav #include <pthread.h>
66cf2b5f3bSDag-Erling Smørgrav /*
67cf2b5f3bSDag-Erling Smørgrav  * Avoid namespace clash when *not* using pthreads for systems *with*
68cf2b5f3bSDag-Erling Smørgrav  * pthreads, which unconditionally define pthread_t via sys/types.h
69cf2b5f3bSDag-Erling Smørgrav  * (e.g. Linux)
70cf2b5f3bSDag-Erling Smørgrav  */
71cf2b5f3bSDag-Erling Smørgrav typedef pthread_t sp_pthread_t;
72cf2b5f3bSDag-Erling Smørgrav #else
731ec0d754SDag-Erling Smørgrav typedef pid_t sp_pthread_t;
741ec0d754SDag-Erling Smørgrav #endif
751ec0d754SDag-Erling Smørgrav 
761ec0d754SDag-Erling Smørgrav struct pam_ctxt {
771ec0d754SDag-Erling Smørgrav 	sp_pthread_t	 pam_thread;
781ec0d754SDag-Erling Smørgrav 	int		 pam_psock;
791ec0d754SDag-Erling Smørgrav 	int		 pam_csock;
801ec0d754SDag-Erling Smørgrav 	int		 pam_done;
811ec0d754SDag-Erling Smørgrav };
821ec0d754SDag-Erling Smørgrav 
831ec0d754SDag-Erling Smørgrav static void sshpam_free_ctx(void *);
841ec0d754SDag-Erling Smørgrav static struct pam_ctxt *cleanup_ctxt;
851ec0d754SDag-Erling Smørgrav 
861ec0d754SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
87cf2b5f3bSDag-Erling Smørgrav /*
88cf2b5f3bSDag-Erling Smørgrav  * Simulate threads with processes.
89cf2b5f3bSDag-Erling Smørgrav  */
901ec0d754SDag-Erling Smørgrav 
911ec0d754SDag-Erling Smørgrav static int sshpam_thread_status = -1;
921ec0d754SDag-Erling Smørgrav static mysig_t sshpam_oldsig;
931ec0d754SDag-Erling Smørgrav 
941ec0d754SDag-Erling Smørgrav static void
951ec0d754SDag-Erling Smørgrav sshpam_sigchld_handler(int sig)
961ec0d754SDag-Erling Smørgrav {
971ec0d754SDag-Erling Smørgrav 	if (cleanup_ctxt == NULL)
981ec0d754SDag-Erling Smørgrav 		return;	/* handler called after PAM cleanup, shouldn't happen */
991ec0d754SDag-Erling Smørgrav 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) == -1)
1001ec0d754SDag-Erling Smørgrav 		return;	/* couldn't wait for process */
1011ec0d754SDag-Erling Smørgrav 	if (WIFSIGNALED(sshpam_thread_status) &&
1021ec0d754SDag-Erling Smørgrav 	    WTERMSIG(sshpam_thread_status) == SIGTERM)
1031ec0d754SDag-Erling Smørgrav 		return;	/* terminated by pthread_cancel */
1041ec0d754SDag-Erling Smørgrav 	if (!WIFEXITED(sshpam_thread_status))
1051ec0d754SDag-Erling Smørgrav 		fatal("PAM: authentication thread exited unexpectedly");
1061ec0d754SDag-Erling Smørgrav 	if (WEXITSTATUS(sshpam_thread_status) != 0)
1071ec0d754SDag-Erling Smørgrav 		fatal("PAM: authentication thread exited uncleanly");
1081ec0d754SDag-Erling Smørgrav }
10909958426SBrian Feldman 
110cf2b5f3bSDag-Erling Smørgrav static void
111cf2b5f3bSDag-Erling Smørgrav pthread_exit(void *value __unused)
11209958426SBrian Feldman {
113cf2b5f3bSDag-Erling Smørgrav 	_exit(0);
11409958426SBrian Feldman }
11509958426SBrian Feldman 
116cf2b5f3bSDag-Erling Smørgrav static int
117cf2b5f3bSDag-Erling Smørgrav pthread_create(sp_pthread_t *thread, const void *attr __unused,
118cf2b5f3bSDag-Erling Smørgrav     void *(*thread_start)(void *), void *arg)
119cf2b5f3bSDag-Erling Smørgrav {
120cf2b5f3bSDag-Erling Smørgrav 	pid_t pid;
121cf2b5f3bSDag-Erling Smørgrav 
1225962c0e9SDag-Erling Smørgrav 	sshpam_thread_status = -1;
123cf2b5f3bSDag-Erling Smørgrav 	switch ((pid = fork())) {
124cf2b5f3bSDag-Erling Smørgrav 	case -1:
125cf2b5f3bSDag-Erling Smørgrav 		error("fork(): %s", strerror(errno));
126cf2b5f3bSDag-Erling Smørgrav 		return (-1);
127cf2b5f3bSDag-Erling Smørgrav 	case 0:
128cf2b5f3bSDag-Erling Smørgrav 		thread_start(arg);
129cf2b5f3bSDag-Erling Smørgrav 		_exit(1);
130cf2b5f3bSDag-Erling Smørgrav 	default:
131cf2b5f3bSDag-Erling Smørgrav 		*thread = pid;
1321ec0d754SDag-Erling Smørgrav 		sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
133cf2b5f3bSDag-Erling Smørgrav 		return (0);
134cf2b5f3bSDag-Erling Smørgrav 	}
135cf2b5f3bSDag-Erling Smørgrav }
136cf2b5f3bSDag-Erling Smørgrav 
137cf2b5f3bSDag-Erling Smørgrav static int
138cf2b5f3bSDag-Erling Smørgrav pthread_cancel(sp_pthread_t thread)
139cf2b5f3bSDag-Erling Smørgrav {
1401ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
141cf2b5f3bSDag-Erling Smørgrav 	return (kill(thread, SIGTERM));
142cf2b5f3bSDag-Erling Smørgrav }
143cf2b5f3bSDag-Erling Smørgrav 
144cf2b5f3bSDag-Erling Smørgrav static int
145cf2b5f3bSDag-Erling Smørgrav pthread_join(sp_pthread_t thread, void **value __unused)
146cf2b5f3bSDag-Erling Smørgrav {
147cf2b5f3bSDag-Erling Smørgrav 	int status;
148cf2b5f3bSDag-Erling Smørgrav 
1491ec0d754SDag-Erling Smørgrav 	if (sshpam_thread_status != -1)
1501ec0d754SDag-Erling Smørgrav 		return (sshpam_thread_status);
1511ec0d754SDag-Erling Smørgrav 	signal(SIGCHLD, sshpam_oldsig);
152cf2b5f3bSDag-Erling Smørgrav 	waitpid(thread, &status, 0);
153cf2b5f3bSDag-Erling Smørgrav 	return (status);
154cf2b5f3bSDag-Erling Smørgrav }
155cf2b5f3bSDag-Erling Smørgrav #endif
156cf2b5f3bSDag-Erling Smørgrav 
157cf2b5f3bSDag-Erling Smørgrav 
158cf2b5f3bSDag-Erling Smørgrav static pam_handle_t *sshpam_handle = NULL;
159cf2b5f3bSDag-Erling Smørgrav static int sshpam_err = 0;
160cf2b5f3bSDag-Erling Smørgrav static int sshpam_authenticated = 0;
161cf2b5f3bSDag-Erling Smørgrav static int sshpam_session_open = 0;
162cf2b5f3bSDag-Erling Smørgrav static int sshpam_cred_established = 0;
1631ec0d754SDag-Erling Smørgrav static int sshpam_account_status = -1;
1641ec0d754SDag-Erling Smørgrav static char **sshpam_env = NULL;
1655962c0e9SDag-Erling Smørgrav static Authctxt *sshpam_authctxt = NULL;
166cf2b5f3bSDag-Erling Smørgrav 
1671ec0d754SDag-Erling Smørgrav /* Some PAM implementations don't implement this */
1681ec0d754SDag-Erling Smørgrav #ifndef HAVE_PAM_GETENVLIST
1691ec0d754SDag-Erling Smørgrav static char **
1701ec0d754SDag-Erling Smørgrav pam_getenvlist(pam_handle_t *pamh)
1711ec0d754SDag-Erling Smørgrav {
1721ec0d754SDag-Erling Smørgrav 	/*
1731ec0d754SDag-Erling Smørgrav 	 * XXX - If necessary, we can still support envrionment passing
1741ec0d754SDag-Erling Smørgrav 	 * for platforms without pam_getenvlist by searching for known
1751ec0d754SDag-Erling Smørgrav 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
1761ec0d754SDag-Erling Smørgrav 	 */
1771ec0d754SDag-Erling Smørgrav 	 return NULL;
1781ec0d754SDag-Erling Smørgrav }
1791ec0d754SDag-Erling Smørgrav #endif
180cf2b5f3bSDag-Erling Smørgrav 
1811ec0d754SDag-Erling Smørgrav void
1821ec0d754SDag-Erling Smørgrav pam_password_change_required(int reqd)
1831ec0d754SDag-Erling Smørgrav {
1841ec0d754SDag-Erling Smørgrav 	debug3("%s %d", __func__, reqd);
1855962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
1865962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
1875962c0e9SDag-Erling Smørgrav 	sshpam_authctxt->force_pwchange = reqd;
1881ec0d754SDag-Erling Smørgrav 	if (reqd) {
1891ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag |= 2;
1901ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag |= 2;
1911ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag |= 2;
1921ec0d754SDag-Erling Smørgrav 	} else {
1931ec0d754SDag-Erling Smørgrav 		no_port_forwarding_flag &= ~2;
1941ec0d754SDag-Erling Smørgrav 		no_agent_forwarding_flag &= ~2;
1951ec0d754SDag-Erling Smørgrav 		no_x11_forwarding_flag &= ~2;
1961ec0d754SDag-Erling Smørgrav 	}
1971ec0d754SDag-Erling Smørgrav }
1981ec0d754SDag-Erling Smørgrav 
1991ec0d754SDag-Erling Smørgrav /* Import regular and PAM environment from subprocess */
2001ec0d754SDag-Erling Smørgrav static void
2011ec0d754SDag-Erling Smørgrav import_environments(Buffer *b)
2021ec0d754SDag-Erling Smørgrav {
2031ec0d754SDag-Erling Smørgrav 	char *env;
2041ec0d754SDag-Erling Smørgrav 	u_int i, num_env;
2051ec0d754SDag-Erling Smørgrav 	int err;
2061ec0d754SDag-Erling Smørgrav 
2071ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
2081ec0d754SDag-Erling Smørgrav 
2095962c0e9SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
2101ec0d754SDag-Erling Smørgrav 	/* Import variables set by do_pam_account */
2111ec0d754SDag-Erling Smørgrav 	sshpam_account_status = buffer_get_int(b);
2121ec0d754SDag-Erling Smørgrav 	pam_password_change_required(buffer_get_int(b));
2131ec0d754SDag-Erling Smørgrav 
2141ec0d754SDag-Erling Smørgrav 	/* Import environment from subprocess */
2151ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
2161ec0d754SDag-Erling Smørgrav 	sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));
2171ec0d754SDag-Erling Smørgrav 	debug3("PAM: num env strings %d", num_env);
2181ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++)
2191ec0d754SDag-Erling Smørgrav 		sshpam_env[i] = buffer_get_string(b, NULL);
2201ec0d754SDag-Erling Smørgrav 
2211ec0d754SDag-Erling Smørgrav 	sshpam_env[num_env] = NULL;
2221ec0d754SDag-Erling Smørgrav 
2231ec0d754SDag-Erling Smørgrav 	/* Import PAM environment from subprocess */
2241ec0d754SDag-Erling Smørgrav 	num_env = buffer_get_int(b);
2251ec0d754SDag-Erling Smørgrav 	debug("PAM: num PAM env strings %d", num_env);
2261ec0d754SDag-Erling Smørgrav 	for(i = 0; i < num_env; i++) {
2271ec0d754SDag-Erling Smørgrav 		env = buffer_get_string(b, NULL);
2281ec0d754SDag-Erling Smørgrav 
2291ec0d754SDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
2301ec0d754SDag-Erling Smørgrav 		/* Errors are not fatal here */
2311ec0d754SDag-Erling Smørgrav 		if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
2321ec0d754SDag-Erling Smørgrav 			error("PAM: pam_putenv: %s",
2331ec0d754SDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
2341ec0d754SDag-Erling Smørgrav 		}
2351ec0d754SDag-Erling Smørgrav #endif
2361ec0d754SDag-Erling Smørgrav 	}
2375962c0e9SDag-Erling Smørgrav #endif
2381ec0d754SDag-Erling Smørgrav }
239cf2b5f3bSDag-Erling Smørgrav 
240cf2b5f3bSDag-Erling Smørgrav /*
241cf2b5f3bSDag-Erling Smørgrav  * Conversation function for authentication thread.
242cf2b5f3bSDag-Erling Smørgrav  */
243cf2b5f3bSDag-Erling Smørgrav static int
244cf2b5f3bSDag-Erling Smørgrav sshpam_thread_conv(int n, const struct pam_message **msg,
245cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
246cf2b5f3bSDag-Erling Smørgrav {
247cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
248cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
249cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
250cf2b5f3bSDag-Erling Smørgrav 	int i;
251cf2b5f3bSDag-Erling Smørgrav 
2521ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
253cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
254cf2b5f3bSDag-Erling Smørgrav 
255cf2b5f3bSDag-Erling Smørgrav 	ctxt = data;
256cf2b5f3bSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
257cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
258cf2b5f3bSDag-Erling Smørgrav 
259cf2b5f3bSDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
260cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
261cf2b5f3bSDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
262cf2b5f3bSDag-Erling Smørgrav 
263cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
264cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
265cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
266cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
267cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
268cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
2691ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
2701ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
2711ec0d754SDag-Erling Smørgrav 				goto fail;
2721ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
2731ec0d754SDag-Erling Smørgrav 				goto fail;
274cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
275cf2b5f3bSDag-Erling Smørgrav 				goto fail;
276cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
27709958426SBrian Feldman 			break;
278cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
279cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
280cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
2811ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
2821ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
2831ec0d754SDag-Erling Smørgrav 				goto fail;
2841ec0d754SDag-Erling Smørgrav 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
2851ec0d754SDag-Erling Smørgrav 				goto fail;
286cf2b5f3bSDag-Erling Smørgrav 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
287cf2b5f3bSDag-Erling Smørgrav 				goto fail;
288cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = buffer_get_string(&buffer, NULL);
289cf2b5f3bSDag-Erling Smørgrav 			break;
290cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
291cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
292cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
2931ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
2941ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
2951ec0d754SDag-Erling Smørgrav 				goto fail;
296cf2b5f3bSDag-Erling Smørgrav 			break;
297cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
298cf2b5f3bSDag-Erling Smørgrav 			buffer_put_cstring(&buffer,
299cf2b5f3bSDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg));
3001ec0d754SDag-Erling Smørgrav 			if (ssh_msg_send(ctxt->pam_csock,
3011ec0d754SDag-Erling Smørgrav 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
3021ec0d754SDag-Erling Smørgrav 				goto fail;
303cf2b5f3bSDag-Erling Smørgrav 			break;
304cf2b5f3bSDag-Erling Smørgrav 		default:
305cf2b5f3bSDag-Erling Smørgrav 			goto fail;
306cf2b5f3bSDag-Erling Smørgrav 		}
307cf2b5f3bSDag-Erling Smørgrav 		buffer_clear(&buffer);
308cf2b5f3bSDag-Erling Smørgrav 	}
309cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
310cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
311cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
312cf2b5f3bSDag-Erling Smørgrav 
313cf2b5f3bSDag-Erling Smørgrav  fail:
314cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
315cf2b5f3bSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
316cf2b5f3bSDag-Erling Smørgrav 			xfree(reply[i].resp);
317cf2b5f3bSDag-Erling Smørgrav 	}
318cf2b5f3bSDag-Erling Smørgrav 	xfree(reply);
319cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
320cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
321cf2b5f3bSDag-Erling Smørgrav }
322cf2b5f3bSDag-Erling Smørgrav 
323cf2b5f3bSDag-Erling Smørgrav /*
324cf2b5f3bSDag-Erling Smørgrav  * Authentication thread.
325cf2b5f3bSDag-Erling Smørgrav  */
326cf2b5f3bSDag-Erling Smørgrav static void *
327cf2b5f3bSDag-Erling Smørgrav sshpam_thread(void *ctxtp)
328cf2b5f3bSDag-Erling Smørgrav {
329cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
330cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
331cf2b5f3bSDag-Erling Smørgrav 	struct pam_conv sshpam_conv;
332cf2b5f3bSDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
3331ec0d754SDag-Erling Smørgrav 	extern char **environ;
3341ec0d754SDag-Erling Smørgrav 	char **env_from_pam;
3351ec0d754SDag-Erling Smørgrav 	u_int i;
336cf2b5f3bSDag-Erling Smørgrav 	const char *pam_user;
337cf2b5f3bSDag-Erling Smørgrav 
338cf2b5f3bSDag-Erling Smørgrav 	pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
339cf2b5f3bSDag-Erling Smørgrav 	setproctitle("%s [pam]", pam_user);
3401ec0d754SDag-Erling Smørgrav 	environ[0] = NULL;
341cf2b5f3bSDag-Erling Smørgrav #endif
342cf2b5f3bSDag-Erling Smørgrav 
343cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.conv = sshpam_thread_conv;
344cf2b5f3bSDag-Erling Smørgrav 	sshpam_conv.appdata_ptr = ctxt;
345cf2b5f3bSDag-Erling Smørgrav 
3465962c0e9SDag-Erling Smørgrav 	if (sshpam_authctxt == NULL)
3475962c0e9SDag-Erling Smørgrav 		fatal("%s: PAM authctxt not initialized", __func__);
3485962c0e9SDag-Erling Smørgrav 
349cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
350cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
351cf2b5f3bSDag-Erling Smørgrav 	    (const void *)&sshpam_conv);
352cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
353cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
354cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_authenticate(sshpam_handle, 0);
355cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
356cf2b5f3bSDag-Erling Smørgrav 		goto auth_fail;
3571ec0d754SDag-Erling Smørgrav 
3581ec0d754SDag-Erling Smørgrav 	if (compat20) {
3591ec0d754SDag-Erling Smørgrav 		if (!do_pam_account())
3601ec0d754SDag-Erling Smørgrav 			goto auth_fail;
3615962c0e9SDag-Erling Smørgrav 		if (sshpam_authctxt->force_pwchange) {
3621ec0d754SDag-Erling Smørgrav 			sshpam_err = pam_chauthtok(sshpam_handle,
3631ec0d754SDag-Erling Smørgrav 			    PAM_CHANGE_EXPIRED_AUTHTOK);
3641ec0d754SDag-Erling Smørgrav 			if (sshpam_err != PAM_SUCCESS)
3651ec0d754SDag-Erling Smørgrav 				goto auth_fail;
3661ec0d754SDag-Erling Smørgrav 			pam_password_change_required(0);
3671ec0d754SDag-Erling Smørgrav 		}
3681ec0d754SDag-Erling Smørgrav 	}
3691ec0d754SDag-Erling Smørgrav 
370cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer, "OK");
3711ec0d754SDag-Erling Smørgrav 
3721ec0d754SDag-Erling Smørgrav #ifndef USE_POSIX_THREADS
3731ec0d754SDag-Erling Smørgrav 	/* Export variables set by do_pam_account */
3741ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_account_status);
3755962c0e9SDag-Erling Smørgrav 	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
3761ec0d754SDag-Erling Smørgrav 
3771ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set in child */
3781ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
3791ec0d754SDag-Erling Smørgrav 		; /* Count */
3801ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
3811ec0d754SDag-Erling Smørgrav 	for(i = 0; environ[i] != NULL; i++)
3821ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, environ[i]);
3831ec0d754SDag-Erling Smørgrav 
3841ec0d754SDag-Erling Smørgrav 	/* Export any environment strings set by PAM in child */
3851ec0d754SDag-Erling Smørgrav 	env_from_pam = pam_getenvlist(sshpam_handle);
3861ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
3871ec0d754SDag-Erling Smørgrav 		; /* Count */
3881ec0d754SDag-Erling Smørgrav 	buffer_put_int(&buffer, i);
3891ec0d754SDag-Erling Smørgrav 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
3901ec0d754SDag-Erling Smørgrav 		buffer_put_cstring(&buffer, env_from_pam[i]);
3911ec0d754SDag-Erling Smørgrav #endif /* USE_POSIX_THREADS */
3921ec0d754SDag-Erling Smørgrav 
3931ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
394cf2b5f3bSDag-Erling Smørgrav 	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
395cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
396cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
397cf2b5f3bSDag-Erling Smørgrav 
398cf2b5f3bSDag-Erling Smørgrav  auth_fail:
399cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer,
400cf2b5f3bSDag-Erling Smørgrav 	    pam_strerror(sshpam_handle, sshpam_err));
4011ec0d754SDag-Erling Smørgrav 	/* XXX - can't do much about an error here */
402cf2b5f3bSDag-Erling Smørgrav 	ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
403cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
404cf2b5f3bSDag-Erling Smørgrav 	pthread_exit(NULL);
405cf2b5f3bSDag-Erling Smørgrav 
406cf2b5f3bSDag-Erling Smørgrav 	return (NULL); /* Avoid warning for non-pthread case */
407cf2b5f3bSDag-Erling Smørgrav }
408cf2b5f3bSDag-Erling Smørgrav 
4091ec0d754SDag-Erling Smørgrav void
4101ec0d754SDag-Erling Smørgrav sshpam_thread_cleanup(void)
411cf2b5f3bSDag-Erling Smørgrav {
4121ec0d754SDag-Erling Smørgrav 	struct pam_ctxt *ctxt = cleanup_ctxt;
413cf2b5f3bSDag-Erling Smørgrav 
4141ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
4151ec0d754SDag-Erling Smørgrav 	if (ctxt != NULL && ctxt->pam_thread != 0) {
416cf2b5f3bSDag-Erling Smørgrav 		pthread_cancel(ctxt->pam_thread);
417cf2b5f3bSDag-Erling Smørgrav 		pthread_join(ctxt->pam_thread, NULL);
418cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_psock);
419cf2b5f3bSDag-Erling Smørgrav 		close(ctxt->pam_csock);
4201ec0d754SDag-Erling Smørgrav 		memset(ctxt, 0, sizeof(*ctxt));
4211ec0d754SDag-Erling Smørgrav 		cleanup_ctxt = NULL;
4221ec0d754SDag-Erling Smørgrav 	}
423cf2b5f3bSDag-Erling Smørgrav }
424cf2b5f3bSDag-Erling Smørgrav 
425cf2b5f3bSDag-Erling Smørgrav static int
426cf2b5f3bSDag-Erling Smørgrav sshpam_null_conv(int n, const struct pam_message **msg,
427cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
428cf2b5f3bSDag-Erling Smørgrav {
4291ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering, %d messages", __func__, n);
430cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
431cf2b5f3bSDag-Erling Smørgrav }
432cf2b5f3bSDag-Erling Smørgrav 
433cf2b5f3bSDag-Erling Smørgrav static struct pam_conv null_conv = { sshpam_null_conv, NULL };
434cf2b5f3bSDag-Erling Smørgrav 
4351ec0d754SDag-Erling Smørgrav void
4361ec0d754SDag-Erling Smørgrav sshpam_cleanup(void)
437cf2b5f3bSDag-Erling Smørgrav {
438cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: cleanup");
439cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_handle == NULL)
440cf2b5f3bSDag-Erling Smørgrav 		return;
441cf2b5f3bSDag-Erling Smørgrav 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
442cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_cred_established) {
443cf2b5f3bSDag-Erling Smørgrav 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
444cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 0;
445cf2b5f3bSDag-Erling Smørgrav 	}
446cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_session_open) {
447cf2b5f3bSDag-Erling Smørgrav 		pam_close_session(sshpam_handle, PAM_SILENT);
448cf2b5f3bSDag-Erling Smørgrav 		sshpam_session_open = 0;
449cf2b5f3bSDag-Erling Smørgrav 	}
4501ec0d754SDag-Erling Smørgrav 	sshpam_authenticated = 0;
451cf2b5f3bSDag-Erling Smørgrav 	pam_end(sshpam_handle, sshpam_err);
452cf2b5f3bSDag-Erling Smørgrav 	sshpam_handle = NULL;
453cf2b5f3bSDag-Erling Smørgrav }
454cf2b5f3bSDag-Erling Smørgrav 
455cf2b5f3bSDag-Erling Smørgrav static int
4565962c0e9SDag-Erling Smørgrav sshpam_init(Authctxt *authctxt)
457cf2b5f3bSDag-Erling Smørgrav {
458cf2b5f3bSDag-Erling Smørgrav 	extern char *__progname;
4595962c0e9SDag-Erling Smørgrav 	const char *pam_rhost, *pam_user, *user = authctxt->user;
460cf2b5f3bSDag-Erling Smørgrav 
461cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_handle != NULL) {
462cf2b5f3bSDag-Erling Smørgrav 		/* We already have a PAM context; check if the user matches */
463cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_get_item(sshpam_handle,
464cf2b5f3bSDag-Erling Smørgrav 		    PAM_USER, (const void **)&pam_user);
465cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
466cf2b5f3bSDag-Erling Smørgrav 			return (0);
467cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
468cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
469cf2b5f3bSDag-Erling Smørgrav 	}
470cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: initializing for \"%s\"", user);
471cf2b5f3bSDag-Erling Smørgrav 	sshpam_err =
472cf2b5f3bSDag-Erling Smørgrav 	    pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
4735962c0e9SDag-Erling Smørgrav 	sshpam_authctxt = authctxt;
4745962c0e9SDag-Erling Smørgrav 
475cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
476cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
477cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
478cf2b5f3bSDag-Erling Smørgrav 		return (-1);
479cf2b5f3bSDag-Erling Smørgrav 	}
480cf2b5f3bSDag-Erling Smørgrav 	pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
481cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
482cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
483cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
484cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
485cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
486cf2b5f3bSDag-Erling Smørgrav 		return (-1);
487cf2b5f3bSDag-Erling Smørgrav 	}
488cf2b5f3bSDag-Erling Smørgrav #ifdef PAM_TTY_KLUDGE
489cf2b5f3bSDag-Erling Smørgrav 	/*
490cf2b5f3bSDag-Erling Smørgrav 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
491cf2b5f3bSDag-Erling Smørgrav 	 * sshd doesn't set the tty until too late in the auth process and
492cf2b5f3bSDag-Erling Smørgrav 	 * may not even set one (for tty-less connections)
493cf2b5f3bSDag-Erling Smørgrav 	 */
494cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: setting PAM_TTY to \"ssh\"");
495cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
496cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS) {
497cf2b5f3bSDag-Erling Smørgrav 		pam_end(sshpam_handle, sshpam_err);
498cf2b5f3bSDag-Erling Smørgrav 		sshpam_handle = NULL;
499cf2b5f3bSDag-Erling Smørgrav 		return (-1);
500cf2b5f3bSDag-Erling Smørgrav 	}
501cf2b5f3bSDag-Erling Smørgrav #endif
502cf2b5f3bSDag-Erling Smørgrav 	return (0);
503cf2b5f3bSDag-Erling Smørgrav }
504cf2b5f3bSDag-Erling Smørgrav 
505cf2b5f3bSDag-Erling Smørgrav static void *
506cf2b5f3bSDag-Erling Smørgrav sshpam_init_ctx(Authctxt *authctxt)
507cf2b5f3bSDag-Erling Smørgrav {
508cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt;
509cf2b5f3bSDag-Erling Smørgrav 	int socks[2];
510cf2b5f3bSDag-Erling Smørgrav 
5111ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
512cf2b5f3bSDag-Erling Smørgrav 	/* Refuse to start if we don't have PAM enabled */
513cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
514cf2b5f3bSDag-Erling Smørgrav 		return NULL;
515cf2b5f3bSDag-Erling Smørgrav 
516cf2b5f3bSDag-Erling Smørgrav 	/* Initialize PAM */
5175962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1) {
518cf2b5f3bSDag-Erling Smørgrav 		error("PAM: initialization failed");
519cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
520cf2b5f3bSDag-Erling Smørgrav 	}
521cf2b5f3bSDag-Erling Smørgrav 
522cf2b5f3bSDag-Erling Smørgrav 	ctxt = xmalloc(sizeof *ctxt);
5231ec0d754SDag-Erling Smørgrav 	memset(ctxt, 0, sizeof(*ctxt));
5241ec0d754SDag-Erling Smørgrav 
525cf2b5f3bSDag-Erling Smørgrav 	/* Start the authentication thread */
526cf2b5f3bSDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
527cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed create sockets: %s", strerror(errno));
528cf2b5f3bSDag-Erling Smørgrav 		xfree(ctxt);
529cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
530cf2b5f3bSDag-Erling Smørgrav 	}
531cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_psock = socks[0];
532cf2b5f3bSDag-Erling Smørgrav 	ctxt->pam_csock = socks[1];
533cf2b5f3bSDag-Erling Smørgrav 	if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
534cf2b5f3bSDag-Erling Smørgrav 		error("PAM: failed to start authentication thread: %s",
535cf2b5f3bSDag-Erling Smørgrav 		    strerror(errno));
536cf2b5f3bSDag-Erling Smørgrav 		close(socks[0]);
537cf2b5f3bSDag-Erling Smørgrav 		close(socks[1]);
538cf2b5f3bSDag-Erling Smørgrav 		xfree(ctxt);
539cf2b5f3bSDag-Erling Smørgrav 		return (NULL);
540cf2b5f3bSDag-Erling Smørgrav 	}
5411ec0d754SDag-Erling Smørgrav 	cleanup_ctxt = ctxt;
542cf2b5f3bSDag-Erling Smørgrav 	return (ctxt);
543cf2b5f3bSDag-Erling Smørgrav }
544cf2b5f3bSDag-Erling Smørgrav 
545cf2b5f3bSDag-Erling Smørgrav static int
546cf2b5f3bSDag-Erling Smørgrav sshpam_query(void *ctx, char **name, char **info,
547cf2b5f3bSDag-Erling Smørgrav     u_int *num, char ***prompts, u_int **echo_on)
548cf2b5f3bSDag-Erling Smørgrav {
549cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
550cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
551cf2b5f3bSDag-Erling Smørgrav 	size_t plen;
552cf2b5f3bSDag-Erling Smørgrav 	u_char type;
553cf2b5f3bSDag-Erling Smørgrav 	char *msg;
554cf2b5f3bSDag-Erling Smørgrav 	size_t len;
555cf2b5f3bSDag-Erling Smørgrav 
5561ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
557cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
558cf2b5f3bSDag-Erling Smørgrav 	*name = xstrdup("");
559cf2b5f3bSDag-Erling Smørgrav 	*info = xstrdup("");
560cf2b5f3bSDag-Erling Smørgrav 	*prompts = xmalloc(sizeof(char *));
561cf2b5f3bSDag-Erling Smørgrav 	**prompts = NULL;
562cf2b5f3bSDag-Erling Smørgrav 	plen = 0;
563cf2b5f3bSDag-Erling Smørgrav 	*echo_on = xmalloc(sizeof(u_int));
564cf2b5f3bSDag-Erling Smørgrav 	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
565cf2b5f3bSDag-Erling Smørgrav 		type = buffer_get_char(&buffer);
566cf2b5f3bSDag-Erling Smørgrav 		msg = buffer_get_string(&buffer, NULL);
567cf2b5f3bSDag-Erling Smørgrav 		switch (type) {
568cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
569cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
570cf2b5f3bSDag-Erling Smørgrav 			*num = 1;
571cf2b5f3bSDag-Erling Smørgrav 			len = plen + strlen(msg) + 1;
572cf2b5f3bSDag-Erling Smørgrav 			**prompts = xrealloc(**prompts, len);
573cf2b5f3bSDag-Erling Smørgrav 			plen += snprintf(**prompts + plen, len, "%s", msg);
574cf2b5f3bSDag-Erling Smørgrav 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
575cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
576cf2b5f3bSDag-Erling Smørgrav 			return (0);
577cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
578cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
579cf2b5f3bSDag-Erling Smørgrav 			/* accumulate messages */
5801ec0d754SDag-Erling Smørgrav 			len = plen + strlen(msg) + 2;
581cf2b5f3bSDag-Erling Smørgrav 			**prompts = xrealloc(**prompts, len);
5821ec0d754SDag-Erling Smørgrav 			plen += snprintf(**prompts + plen, len, "%s\n", msg);
583cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
584cf2b5f3bSDag-Erling Smørgrav 			break;
585cf2b5f3bSDag-Erling Smørgrav 		case PAM_SUCCESS:
586cf2b5f3bSDag-Erling Smørgrav 		case PAM_AUTH_ERR:
587cf2b5f3bSDag-Erling Smørgrav 			if (**prompts != NULL) {
588cf2b5f3bSDag-Erling Smørgrav 				/* drain any accumulated messages */
5891ec0d754SDag-Erling Smørgrav 				debug("PAM: %s", **prompts);
5901ec0d754SDag-Erling Smørgrav 				buffer_append(&loginmsg, **prompts,
5911ec0d754SDag-Erling Smørgrav 				    strlen(**prompts));
592cf2b5f3bSDag-Erling Smørgrav 				xfree(**prompts);
593cf2b5f3bSDag-Erling Smørgrav 				**prompts = NULL;
594cf2b5f3bSDag-Erling Smørgrav 			}
595cf2b5f3bSDag-Erling Smørgrav 			if (type == PAM_SUCCESS) {
5961ec0d754SDag-Erling Smørgrav 				import_environments(&buffer);
597cf2b5f3bSDag-Erling Smørgrav 				*num = 0;
598cf2b5f3bSDag-Erling Smørgrav 				**echo_on = 0;
599cf2b5f3bSDag-Erling Smørgrav 				ctxt->pam_done = 1;
600cf2b5f3bSDag-Erling Smørgrav 				xfree(msg);
601cf2b5f3bSDag-Erling Smørgrav 				return (0);
602cf2b5f3bSDag-Erling Smørgrav 			}
6035962c0e9SDag-Erling Smørgrav 			error("PAM: %s for %s%.100s from %.100s", msg,
6045962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->valid ? "" : "illegal user ",
6055962c0e9SDag-Erling Smørgrav 			    sshpam_authctxt->user,
6065962c0e9SDag-Erling Smørgrav 			    get_remote_name_or_ip(utmp_len, options.use_dns));
6071ec0d754SDag-Erling Smørgrav 			/* FALLTHROUGH */
608cf2b5f3bSDag-Erling Smørgrav 		default:
609cf2b5f3bSDag-Erling Smørgrav 			*num = 0;
610cf2b5f3bSDag-Erling Smørgrav 			**echo_on = 0;
611cf2b5f3bSDag-Erling Smørgrav 			xfree(msg);
612cf2b5f3bSDag-Erling Smørgrav 			ctxt->pam_done = -1;
613cf2b5f3bSDag-Erling Smørgrav 			return (-1);
614cf2b5f3bSDag-Erling Smørgrav 		}
615cf2b5f3bSDag-Erling Smørgrav 	}
616cf2b5f3bSDag-Erling Smørgrav 	return (-1);
617cf2b5f3bSDag-Erling Smørgrav }
618cf2b5f3bSDag-Erling Smørgrav 
619cf2b5f3bSDag-Erling Smørgrav /* XXX - see also comment in auth-chall.c:verify_response */
620cf2b5f3bSDag-Erling Smørgrav static int
621cf2b5f3bSDag-Erling Smørgrav sshpam_respond(void *ctx, u_int num, char **resp)
622cf2b5f3bSDag-Erling Smørgrav {
623cf2b5f3bSDag-Erling Smørgrav 	Buffer buffer;
624cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctx;
625cf2b5f3bSDag-Erling Smørgrav 
6261ec0d754SDag-Erling Smørgrav 	debug2("PAM: %s entering, %d responses", __func__, num);
627cf2b5f3bSDag-Erling Smørgrav 	switch (ctxt->pam_done) {
628cf2b5f3bSDag-Erling Smørgrav 	case 1:
629cf2b5f3bSDag-Erling Smørgrav 		sshpam_authenticated = 1;
630cf2b5f3bSDag-Erling Smørgrav 		return (0);
631cf2b5f3bSDag-Erling Smørgrav 	case 0:
632cf2b5f3bSDag-Erling Smørgrav 		break;
633cf2b5f3bSDag-Erling Smørgrav 	default:
634cf2b5f3bSDag-Erling Smørgrav 		return (-1);
635cf2b5f3bSDag-Erling Smørgrav 	}
636cf2b5f3bSDag-Erling Smørgrav 	if (num != 1) {
637cf2b5f3bSDag-Erling Smørgrav 		error("PAM: expected one response, got %u", num);
638cf2b5f3bSDag-Erling Smørgrav 		return (-1);
639cf2b5f3bSDag-Erling Smørgrav 	}
640cf2b5f3bSDag-Erling Smørgrav 	buffer_init(&buffer);
641cf2b5f3bSDag-Erling Smørgrav 	buffer_put_cstring(&buffer, *resp);
6421ec0d754SDag-Erling Smørgrav 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
6431ec0d754SDag-Erling Smørgrav 		buffer_free(&buffer);
6441ec0d754SDag-Erling Smørgrav 		return (-1);
6451ec0d754SDag-Erling Smørgrav 	}
646cf2b5f3bSDag-Erling Smørgrav 	buffer_free(&buffer);
647cf2b5f3bSDag-Erling Smørgrav 	return (1);
648cf2b5f3bSDag-Erling Smørgrav }
649cf2b5f3bSDag-Erling Smørgrav 
650cf2b5f3bSDag-Erling Smørgrav static void
651cf2b5f3bSDag-Erling Smørgrav sshpam_free_ctx(void *ctxtp)
652cf2b5f3bSDag-Erling Smørgrav {
653cf2b5f3bSDag-Erling Smørgrav 	struct pam_ctxt *ctxt = ctxtp;
654cf2b5f3bSDag-Erling Smørgrav 
6551ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s entering", __func__);
6561ec0d754SDag-Erling Smørgrav 	sshpam_thread_cleanup();
657cf2b5f3bSDag-Erling Smørgrav 	xfree(ctxt);
658cf2b5f3bSDag-Erling Smørgrav 	/*
659cf2b5f3bSDag-Erling Smørgrav 	 * We don't call sshpam_cleanup() here because we may need the PAM
660cf2b5f3bSDag-Erling Smørgrav 	 * handle at a later stage, e.g. when setting up a session.  It's
661cf2b5f3bSDag-Erling Smørgrav 	 * still on the cleanup list, so pam_end() *will* be called before
662cf2b5f3bSDag-Erling Smørgrav 	 * the server process terminates.
663cf2b5f3bSDag-Erling Smørgrav 	 */
664cf2b5f3bSDag-Erling Smørgrav }
665cf2b5f3bSDag-Erling Smørgrav 
666cf2b5f3bSDag-Erling Smørgrav KbdintDevice sshpam_device = {
667cf2b5f3bSDag-Erling Smørgrav 	"pam",
668cf2b5f3bSDag-Erling Smørgrav 	sshpam_init_ctx,
669cf2b5f3bSDag-Erling Smørgrav 	sshpam_query,
670cf2b5f3bSDag-Erling Smørgrav 	sshpam_respond,
671cf2b5f3bSDag-Erling Smørgrav 	sshpam_free_ctx
672cf2b5f3bSDag-Erling Smørgrav };
673cf2b5f3bSDag-Erling Smørgrav 
674cf2b5f3bSDag-Erling Smørgrav KbdintDevice mm_sshpam_device = {
675cf2b5f3bSDag-Erling Smørgrav 	"pam",
676cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_init_ctx,
677cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_query,
678cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_respond,
679cf2b5f3bSDag-Erling Smørgrav 	mm_sshpam_free_ctx
680cf2b5f3bSDag-Erling Smørgrav };
681cf2b5f3bSDag-Erling Smørgrav 
682cf2b5f3bSDag-Erling Smørgrav /*
683cf2b5f3bSDag-Erling Smørgrav  * This replaces auth-pam.c
684cf2b5f3bSDag-Erling Smørgrav  */
685cf2b5f3bSDag-Erling Smørgrav void
6865962c0e9SDag-Erling Smørgrav start_pam(Authctxt *authctxt)
687cf2b5f3bSDag-Erling Smørgrav {
688cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
689cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation requested when UsePAM=no");
690cf2b5f3bSDag-Erling Smørgrav 
6915962c0e9SDag-Erling Smørgrav 	if (sshpam_init(authctxt) == -1)
692cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: initialisation failed");
693cf2b5f3bSDag-Erling Smørgrav }
694cf2b5f3bSDag-Erling Smørgrav 
695cf2b5f3bSDag-Erling Smørgrav void
696cf2b5f3bSDag-Erling Smørgrav finish_pam(void)
697cf2b5f3bSDag-Erling Smørgrav {
6981ec0d754SDag-Erling Smørgrav 	sshpam_cleanup();
699cf2b5f3bSDag-Erling Smørgrav }
700cf2b5f3bSDag-Erling Smørgrav 
701cf2b5f3bSDag-Erling Smørgrav u_int
702cf2b5f3bSDag-Erling Smørgrav do_pam_account(void)
703cf2b5f3bSDag-Erling Smørgrav {
7041ec0d754SDag-Erling Smørgrav 	if (sshpam_account_status != -1)
7051ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
7061ec0d754SDag-Erling Smørgrav 
707cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
7081ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s pam_acct_mgmt = %d", __func__, sshpam_err);
709cf2b5f3bSDag-Erling Smørgrav 
7101ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
7111ec0d754SDag-Erling Smørgrav 		sshpam_account_status = 0;
7121ec0d754SDag-Erling Smørgrav 		return (sshpam_account_status);
71309958426SBrian Feldman 	}
71409958426SBrian Feldman 
7151ec0d754SDag-Erling Smørgrav 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
7161ec0d754SDag-Erling Smørgrav 		pam_password_change_required(1);
71709958426SBrian Feldman 
7181ec0d754SDag-Erling Smørgrav 	sshpam_account_status = 1;
7191ec0d754SDag-Erling Smørgrav 	return (sshpam_account_status);
72009958426SBrian Feldman }
72109958426SBrian Feldman 
722cf2b5f3bSDag-Erling Smørgrav void
723cf2b5f3bSDag-Erling Smørgrav do_pam_set_tty(const char *tty)
724cf2b5f3bSDag-Erling Smørgrav {
725cf2b5f3bSDag-Erling Smørgrav 	if (tty != NULL) {
726cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: setting PAM_TTY to \"%s\"", tty);
727cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
728cf2b5f3bSDag-Erling Smørgrav 		if (sshpam_err != PAM_SUCCESS)
729cf2b5f3bSDag-Erling Smørgrav 			fatal("PAM: failed to set PAM_TTY: %s",
730cf2b5f3bSDag-Erling Smørgrav 			    pam_strerror(sshpam_handle, sshpam_err));
731cf2b5f3bSDag-Erling Smørgrav 	}
73209958426SBrian Feldman }
73309958426SBrian Feldman 
734cf2b5f3bSDag-Erling Smørgrav void
735cf2b5f3bSDag-Erling Smørgrav do_pam_setcred(int init)
73609958426SBrian Feldman {
737cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
738cf2b5f3bSDag-Erling Smørgrav 	    (const void *)&null_conv);
739cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
740cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
741cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
742cf2b5f3bSDag-Erling Smørgrav 	if (init) {
743cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: establishing credentials");
744cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
745cf2b5f3bSDag-Erling Smørgrav 	} else {
746cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: reinitializing credentials");
747cf2b5f3bSDag-Erling Smørgrav 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
748cf2b5f3bSDag-Erling Smørgrav 	}
749cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err == PAM_SUCCESS) {
750cf2b5f3bSDag-Erling Smørgrav 		sshpam_cred_established = 1;
751989dd127SDag-Erling Smørgrav 		return;
752cf2b5f3bSDag-Erling Smørgrav 	}
753cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_authenticated)
754cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_setcred(): %s",
755cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
7562c917d39SAlfred Perlstein 	else
757cf2b5f3bSDag-Erling Smørgrav 		debug("PAM: pam_setcred(): %s",
758cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
75909958426SBrian Feldman }
76009958426SBrian Feldman 
761cf2b5f3bSDag-Erling Smørgrav static int
7621ec0d754SDag-Erling Smørgrav pam_tty_conv(int n, const struct pam_message **msg,
763cf2b5f3bSDag-Erling Smørgrav     struct pam_response **resp, void *data)
76409958426SBrian Feldman {
765cf2b5f3bSDag-Erling Smørgrav 	char input[PAM_MAX_MSG_SIZE];
766cf2b5f3bSDag-Erling Smørgrav 	struct pam_response *reply;
767f388f5efSDag-Erling Smørgrav 	int i;
768f388f5efSDag-Erling Smørgrav 
7691ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
7701ec0d754SDag-Erling Smørgrav 
771cf2b5f3bSDag-Erling Smørgrav 	*resp = NULL;
772cf2b5f3bSDag-Erling Smørgrav 
7731ec0d754SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
774cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
775cf2b5f3bSDag-Erling Smørgrav 
776cf2b5f3bSDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
777cf2b5f3bSDag-Erling Smørgrav 		return (PAM_CONV_ERR);
778cf2b5f3bSDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
779cf2b5f3bSDag-Erling Smørgrav 
780cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
781cf2b5f3bSDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
782cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
783cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp =
784cf2b5f3bSDag-Erling Smørgrav 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
785cf2b5f3bSDag-Erling Smørgrav 			    RP_ALLOW_STDIN);
786cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
787cf2b5f3bSDag-Erling Smørgrav 			break;
788cf2b5f3bSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
7891ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
790cf2b5f3bSDag-Erling Smørgrav 			fgets(input, sizeof input, stdin);
791cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp = xstrdup(input);
792cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
793cf2b5f3bSDag-Erling Smørgrav 			break;
794cf2b5f3bSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
795cf2b5f3bSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
7961ec0d754SDag-Erling Smørgrav 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
797cf2b5f3bSDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
798cf2b5f3bSDag-Erling Smørgrav 			break;
799cf2b5f3bSDag-Erling Smørgrav 		default:
800cf2b5f3bSDag-Erling Smørgrav 			goto fail;
801f388f5efSDag-Erling Smørgrav 		}
802f388f5efSDag-Erling Smørgrav 	}
803cf2b5f3bSDag-Erling Smørgrav 	*resp = reply;
804cf2b5f3bSDag-Erling Smørgrav 	return (PAM_SUCCESS);
805cf2b5f3bSDag-Erling Smørgrav 
806cf2b5f3bSDag-Erling Smørgrav  fail:
807cf2b5f3bSDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
808cf2b5f3bSDag-Erling Smørgrav 		if (reply[i].resp != NULL)
809cf2b5f3bSDag-Erling Smørgrav 			xfree(reply[i].resp);
810cf2b5f3bSDag-Erling Smørgrav 	}
811cf2b5f3bSDag-Erling Smørgrav 	xfree(reply);
812cf2b5f3bSDag-Erling Smørgrav 	return (PAM_CONV_ERR);
813cf2b5f3bSDag-Erling Smørgrav }
814f388f5efSDag-Erling Smørgrav 
8151ec0d754SDag-Erling Smørgrav static struct pam_conv tty_conv = { pam_tty_conv, NULL };
8161ec0d754SDag-Erling Smørgrav 
817cf2b5f3bSDag-Erling Smørgrav /*
818cf2b5f3bSDag-Erling Smørgrav  * XXX this should be done in the authentication phase, but ssh1 doesn't
819cf2b5f3bSDag-Erling Smørgrav  * support that
820cf2b5f3bSDag-Erling Smørgrav  */
821cf2b5f3bSDag-Erling Smørgrav void
822cf2b5f3bSDag-Erling Smørgrav do_pam_chauthtok(void)
82309958426SBrian Feldman {
824cf2b5f3bSDag-Erling Smørgrav 	if (use_privsep)
825cf2b5f3bSDag-Erling Smørgrav 		fatal("Password expired (unable to change with privsep)");
826cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
8271ec0d754SDag-Erling Smørgrav 	    (const void *)&tty_conv);
828cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
829cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
830cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
831cf2b5f3bSDag-Erling Smørgrav 	debug("PAM: changing password");
832cf2b5f3bSDag-Erling Smørgrav 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
833cf2b5f3bSDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
834cf2b5f3bSDag-Erling Smørgrav 		fatal("PAM: pam_chauthtok(): %s",
835cf2b5f3bSDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
83609958426SBrian Feldman }
83709958426SBrian Feldman 
8381ec0d754SDag-Erling Smørgrav static int
8391ec0d754SDag-Erling Smørgrav pam_store_conv(int n, const struct pam_message **msg,
8401ec0d754SDag-Erling Smørgrav     struct pam_response **resp, void *data)
8411ec0d754SDag-Erling Smørgrav {
8421ec0d754SDag-Erling Smørgrav 	struct pam_response *reply;
8431ec0d754SDag-Erling Smørgrav 	int i;
8441ec0d754SDag-Erling Smørgrav 	size_t len;
8451ec0d754SDag-Erling Smørgrav 
8461ec0d754SDag-Erling Smørgrav 	debug3("PAM: %s called with %d messages", __func__, n);
8471ec0d754SDag-Erling Smørgrav 	*resp = NULL;
8481ec0d754SDag-Erling Smørgrav 
8491ec0d754SDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
8501ec0d754SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
8511ec0d754SDag-Erling Smørgrav 
8521ec0d754SDag-Erling Smørgrav 	if ((reply = malloc(n * sizeof(*reply))) == NULL)
8531ec0d754SDag-Erling Smørgrav 		return (PAM_CONV_ERR);
8541ec0d754SDag-Erling Smørgrav 	memset(reply, 0, n * sizeof(*reply));
8551ec0d754SDag-Erling Smørgrav 
8561ec0d754SDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
8571ec0d754SDag-Erling Smørgrav 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
8581ec0d754SDag-Erling Smørgrav 		case PAM_ERROR_MSG:
8591ec0d754SDag-Erling Smørgrav 		case PAM_TEXT_INFO:
8601ec0d754SDag-Erling Smørgrav 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
8611ec0d754SDag-Erling Smørgrav 			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
8621ec0d754SDag-Erling Smørgrav 			buffer_append(&loginmsg, "\n", 1 );
8631ec0d754SDag-Erling Smørgrav 			reply[i].resp_retcode = PAM_SUCCESS;
8641ec0d754SDag-Erling Smørgrav 			break;
8651ec0d754SDag-Erling Smørgrav 		default:
8661ec0d754SDag-Erling Smørgrav 			goto fail;
8671ec0d754SDag-Erling Smørgrav 		}
8681ec0d754SDag-Erling Smørgrav 	}
8691ec0d754SDag-Erling Smørgrav 	*resp = reply;
8701ec0d754SDag-Erling Smørgrav 	return (PAM_SUCCESS);
8711ec0d754SDag-Erling Smørgrav 
8721ec0d754SDag-Erling Smørgrav  fail:
8731ec0d754SDag-Erling Smørgrav 	for(i = 0; i < n; i++) {
8741ec0d754SDag-Erling Smørgrav 		if (reply[i].resp != NULL)
8751ec0d754SDag-Erling Smørgrav 			xfree(reply[i].resp);
8761ec0d754SDag-Erling Smørgrav 	}
8771ec0d754SDag-Erling Smørgrav 	xfree(reply);
8781ec0d754SDag-Erling Smørgrav 	return (PAM_CONV_ERR);
8791ec0d754SDag-Erling Smørgrav }
8801ec0d754SDag-Erling Smørgrav 
8811ec0d754SDag-Erling Smørgrav static struct pam_conv store_conv = { pam_store_conv, NULL };
8821ec0d754SDag-Erling Smørgrav 
8831ec0d754SDag-Erling Smørgrav void
8841ec0d754SDag-Erling Smørgrav do_pam_session(void)
8851ec0d754SDag-Erling Smørgrav {
8861ec0d754SDag-Erling Smørgrav 	debug3("PAM: opening session");
8871ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
8881ec0d754SDag-Erling Smørgrav 	    (const void *)&store_conv);
8891ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
8901ec0d754SDag-Erling Smørgrav 		fatal("PAM: failed to set PAM_CONV: %s",
8911ec0d754SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
8921ec0d754SDag-Erling Smørgrav 	sshpam_err = pam_open_session(sshpam_handle, 0);
8931ec0d754SDag-Erling Smørgrav 	if (sshpam_err != PAM_SUCCESS)
8941ec0d754SDag-Erling Smørgrav 		fatal("PAM: pam_open_session(): %s",
8951ec0d754SDag-Erling Smørgrav 		    pam_strerror(sshpam_handle, sshpam_err));
8961ec0d754SDag-Erling Smørgrav 	sshpam_session_open = 1;
8971ec0d754SDag-Erling Smørgrav }
8981ec0d754SDag-Erling Smørgrav 
899cf2b5f3bSDag-Erling Smørgrav /*
900cf2b5f3bSDag-Erling Smørgrav  * Set a PAM environment string. We need to do this so that the session
901cf2b5f3bSDag-Erling Smørgrav  * modules can handle things like Kerberos/GSI credentials that appear
902cf2b5f3bSDag-Erling Smørgrav  * during the ssh authentication process.
903cf2b5f3bSDag-Erling Smørgrav  */
904cf2b5f3bSDag-Erling Smørgrav int
905cf2b5f3bSDag-Erling Smørgrav do_pam_putenv(char *name, char *value)
90609958426SBrian Feldman {
907cf2b5f3bSDag-Erling Smørgrav 	int ret = 1;
908cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_PAM_PUTENV
909cf2b5f3bSDag-Erling Smørgrav 	char *compound;
910cf2b5f3bSDag-Erling Smørgrav 	size_t len;
91109958426SBrian Feldman 
912cf2b5f3bSDag-Erling Smørgrav 	len = strlen(name) + strlen(value) + 2;
913cf2b5f3bSDag-Erling Smørgrav 	compound = xmalloc(len);
91409958426SBrian Feldman 
915cf2b5f3bSDag-Erling Smørgrav 	snprintf(compound, len, "%s=%s", name, value);
916cf2b5f3bSDag-Erling Smørgrav 	ret = pam_putenv(sshpam_handle, compound);
917cf2b5f3bSDag-Erling Smørgrav 	xfree(compound);
918cf2b5f3bSDag-Erling Smørgrav #endif
91909958426SBrian Feldman 
920cf2b5f3bSDag-Erling Smørgrav 	return (ret);
921cf2b5f3bSDag-Erling Smørgrav }
92209958426SBrian Feldman 
9231ec0d754SDag-Erling Smørgrav char **
9241ec0d754SDag-Erling Smørgrav fetch_pam_child_environment(void)
925cf2b5f3bSDag-Erling Smørgrav {
9261ec0d754SDag-Erling Smørgrav 	return sshpam_env;
927cf2b5f3bSDag-Erling Smørgrav }
928cf2b5f3bSDag-Erling Smørgrav 
929cf2b5f3bSDag-Erling Smørgrav char **
930cf2b5f3bSDag-Erling Smørgrav fetch_pam_environment(void)
931cf2b5f3bSDag-Erling Smørgrav {
932cf2b5f3bSDag-Erling Smørgrav 	return (pam_getenvlist(sshpam_handle));
933cf2b5f3bSDag-Erling Smørgrav }
934cf2b5f3bSDag-Erling Smørgrav 
935cf2b5f3bSDag-Erling Smørgrav void
936cf2b5f3bSDag-Erling Smørgrav free_pam_environment(char **env)
937cf2b5f3bSDag-Erling Smørgrav {
938cf2b5f3bSDag-Erling Smørgrav 	char **envp;
939cf2b5f3bSDag-Erling Smørgrav 
940cf2b5f3bSDag-Erling Smørgrav 	if (env == NULL)
941cf2b5f3bSDag-Erling Smørgrav 		return;
942cf2b5f3bSDag-Erling Smørgrav 
943cf2b5f3bSDag-Erling Smørgrav 	for (envp = env; *envp; envp++)
944cf2b5f3bSDag-Erling Smørgrav 		xfree(*envp);
945cf2b5f3bSDag-Erling Smørgrav 	xfree(env);
94609958426SBrian Feldman }
94709958426SBrian Feldman 
94809958426SBrian Feldman #endif /* USE_PAM */
949