xref: /freebsd/crypto/openssh/auth-pam.c (revision 3f68b24e10aeb1a1cd85f2d349da44138d52c501)
1 /*-
2  * Copyright (c) 2002 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
5  * This software was developed for the FreeBSD Project by ThinkSec AS and
6  * NAI Labs, the Security Research Division of Network Associates, Inc.
7  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8  * DARPA CHATS research program.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 /*
32  * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
33  * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
34  *
35  * Permission to use, copy, modify, and distribute this software for any
36  * purpose with or without fee is hereby granted, provided that the above
37  * copyright notice and this permission notice appear in all copies.
38  *
39  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
40  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
41  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
42  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
44  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
45  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 /* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */
49 
50 #include "includes.h"
51 
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/wait.h>
55 
56 #include <errno.h>
57 #include <signal.h>
58 #include <stdarg.h>
59 #include <string.h>
60 #include <unistd.h>
61 
62 #ifdef USE_PAM
63 #if defined(HAVE_SECURITY_PAM_APPL_H)
64 #include <security/pam_appl.h>
65 #elif defined (HAVE_PAM_PAM_APPL_H)
66 #include <pam/pam_appl.h>
67 #endif
68 
69 /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
70 #ifdef PAM_SUN_CODEBASE
71 # define sshpam_const		/* Solaris, HP-UX, AIX */
72 #else
73 # define sshpam_const	const	/* LinuxPAM, OpenPAM */
74 #endif
75 
76 /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
77 #ifdef PAM_SUN_CODEBASE
78 # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
79 #else
80 # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
81 #endif
82 
83 #include "xmalloc.h"
84 #include "buffer.h"
85 #include "key.h"
86 #include "hostfile.h"
87 #include "auth.h"
88 #include "auth-pam.h"
89 #include "canohost.h"
90 #include "log.h"
91 #include "msg.h"
92 #include "packet.h"
93 #include "misc.h"
94 #include "servconf.h"
95 #include "ssh2.h"
96 #include "auth-options.h"
97 #ifdef GSSAPI
98 #include "ssh-gss.h"
99 #endif
100 #include "monitor_wrap.h"
101 #ifdef USE_BLACKLIST
102 #include "blacklist_client.h"
103 #endif
104 
105 extern ServerOptions options;
106 extern Buffer loginmsg;
107 extern int compat20;
108 extern u_int utmp_len;
109 
110 /* so we don't silently change behaviour */
111 #ifdef USE_POSIX_THREADS
112 # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
113 #endif
114 
115 /*
116  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
117  * and generally a bad idea.  Use at own risk and do not expect support if
118  * this breaks.
119  */
120 #ifdef UNSUPPORTED_POSIX_THREADS_HACK
121 #include <pthread.h>
122 /*
123  * Avoid namespace clash when *not* using pthreads for systems *with*
124  * pthreads, which unconditionally define pthread_t via sys/types.h
125  * (e.g. Linux)
126  */
127 typedef pthread_t sp_pthread_t;
128 #else
129 typedef pid_t sp_pthread_t;
130 #endif
131 
132 struct pam_ctxt {
133 	sp_pthread_t	 pam_thread;
134 	int		 pam_psock;
135 	int		 pam_csock;
136 	int		 pam_done;
137 };
138 
139 static void sshpam_free_ctx(void *);
140 static struct pam_ctxt *cleanup_ctxt;
141 
142 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
143 /*
144  * Simulate threads with processes.
145  */
146 
147 static int sshpam_thread_status = -1;
148 static mysig_t sshpam_oldsig;
149 
150 static void
151 sshpam_sigchld_handler(int sig)
152 {
153 	signal(SIGCHLD, SIG_DFL);
154 	if (cleanup_ctxt == NULL)
155 		return;	/* handler called after PAM cleanup, shouldn't happen */
156 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
157 	    <= 0) {
158 		/* PAM thread has not exitted, privsep slave must have */
159 		kill(cleanup_ctxt->pam_thread, SIGTERM);
160 		if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
161 		    <= 0)
162 			return; /* could not wait */
163 	}
164 	if (WIFSIGNALED(sshpam_thread_status) &&
165 	    WTERMSIG(sshpam_thread_status) == SIGTERM)
166 		return;	/* terminated by pthread_cancel */
167 	if (!WIFEXITED(sshpam_thread_status))
168 		sigdie("PAM: authentication thread exited unexpectedly");
169 	if (WEXITSTATUS(sshpam_thread_status) != 0)
170 		sigdie("PAM: authentication thread exited uncleanly");
171 }
172 
173 /* ARGSUSED */
174 static void
175 pthread_exit(void *value)
176 {
177 	_exit(0);
178 }
179 
180 /* ARGSUSED */
181 static int
182 pthread_create(sp_pthread_t *thread, const void *attr,
183     void *(*thread_start)(void *), void *arg)
184 {
185 	pid_t pid;
186 	struct pam_ctxt *ctx = arg;
187 
188 	sshpam_thread_status = -1;
189 	switch ((pid = fork())) {
190 	case -1:
191 		error("fork(): %s", strerror(errno));
192 		return (-1);
193 	case 0:
194 		close(ctx->pam_psock);
195 		ctx->pam_psock = -1;
196 		thread_start(arg);
197 		_exit(1);
198 	default:
199 		*thread = pid;
200 		close(ctx->pam_csock);
201 		ctx->pam_csock = -1;
202 		sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);
203 		return (0);
204 	}
205 }
206 
207 static int
208 pthread_cancel(sp_pthread_t thread)
209 {
210 	signal(SIGCHLD, sshpam_oldsig);
211 	return (kill(thread, SIGTERM));
212 }
213 
214 /* ARGSUSED */
215 static int
216 pthread_join(sp_pthread_t thread, void **value)
217 {
218 	int status;
219 
220 	if (sshpam_thread_status != -1)
221 		return (sshpam_thread_status);
222 	signal(SIGCHLD, sshpam_oldsig);
223 	waitpid(thread, &status, 0);
224 	return (status);
225 }
226 #endif
227 
228 
229 static pam_handle_t *sshpam_handle = NULL;
230 static int sshpam_err = 0;
231 static int sshpam_authenticated = 0;
232 static int sshpam_session_open = 0;
233 static int sshpam_cred_established = 0;
234 static int sshpam_account_status = -1;
235 static char **sshpam_env = NULL;
236 static Authctxt *sshpam_authctxt = NULL;
237 static const char *sshpam_password = NULL;
238 static char badpw[] = "\b\n\r\177INCORRECT";
239 
240 /* Some PAM implementations don't implement this */
241 #ifndef HAVE_PAM_GETENVLIST
242 static char **
243 pam_getenvlist(pam_handle_t *pamh)
244 {
245 	/*
246 	 * XXX - If necessary, we can still support envrionment passing
247 	 * for platforms without pam_getenvlist by searching for known
248 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
249 	 */
250 	 return NULL;
251 }
252 #endif
253 
254 /*
255  * Some platforms, notably Solaris, do not enforce password complexity
256  * rules during pam_chauthtok() if the real uid of the calling process
257  * is 0, on the assumption that it's being called by "passwd" run by root.
258  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
259  * the right thing.
260  */
261 #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
262 static int
263 sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
264 {
265 	int result;
266 
267 	if (sshpam_authctxt == NULL)
268 		fatal("PAM: sshpam_authctxt not initialized");
269 	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
270 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
271 	result = pam_chauthtok(pamh, flags);
272 	if (setreuid(0, -1) == -1)
273 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
274 	return result;
275 }
276 # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
277 #endif
278 
279 void
280 sshpam_password_change_required(int reqd)
281 {
282 	debug3("%s %d", __func__, reqd);
283 	if (sshpam_authctxt == NULL)
284 		fatal("%s: PAM authctxt not initialized", __func__);
285 	sshpam_authctxt->force_pwchange = reqd;
286 	if (reqd) {
287 		no_port_forwarding_flag |= 2;
288 		no_agent_forwarding_flag |= 2;
289 		no_x11_forwarding_flag |= 2;
290 	} else {
291 		no_port_forwarding_flag &= ~2;
292 		no_agent_forwarding_flag &= ~2;
293 		no_x11_forwarding_flag &= ~2;
294 	}
295 }
296 
297 /* Import regular and PAM environment from subprocess */
298 static void
299 import_environments(Buffer *b)
300 {
301 	char *env;
302 	u_int i, num_env;
303 	int err;
304 
305 	debug3("PAM: %s entering", __func__);
306 
307 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
308 	/* Import variables set by do_pam_account */
309 	sshpam_account_status = buffer_get_int(b);
310 	sshpam_password_change_required(buffer_get_int(b));
311 
312 	/* Import environment from subprocess */
313 	num_env = buffer_get_int(b);
314 	if (num_env > 1024)
315 		fatal("%s: received %u environment variables, expected <= 1024",
316 		    __func__, num_env);
317 	sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env));
318 	debug3("PAM: num env strings %d", num_env);
319 	for(i = 0; i < num_env; i++)
320 		sshpam_env[i] = buffer_get_string(b, NULL);
321 
322 	sshpam_env[num_env] = NULL;
323 
324 	/* Import PAM environment from subprocess */
325 	num_env = buffer_get_int(b);
326 	debug("PAM: num PAM env strings %d", num_env);
327 	for(i = 0; i < num_env; i++) {
328 		env = buffer_get_string(b, NULL);
329 
330 #ifdef HAVE_PAM_PUTENV
331 		/* Errors are not fatal here */
332 		if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
333 			error("PAM: pam_putenv: %s",
334 			    pam_strerror(sshpam_handle, sshpam_err));
335 		}
336 #endif
337 	}
338 #endif
339 }
340 
341 /*
342  * Conversation function for authentication thread.
343  */
344 static int
345 sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
346     struct pam_response **resp, void *data)
347 {
348 	Buffer buffer;
349 	struct pam_ctxt *ctxt;
350 	struct pam_response *reply;
351 	int i;
352 
353 	debug3("PAM: %s entering, %d messages", __func__, n);
354 	*resp = NULL;
355 
356 	if (data == NULL) {
357 		error("PAM: conversation function passed a null context");
358 		return (PAM_CONV_ERR);
359 	}
360 	ctxt = data;
361 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
362 		return (PAM_CONV_ERR);
363 
364 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
365 		return (PAM_CONV_ERR);
366 
367 	buffer_init(&buffer);
368 	for (i = 0; i < n; ++i) {
369 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
370 		case PAM_PROMPT_ECHO_OFF:
371 			buffer_put_cstring(&buffer,
372 			    PAM_MSG_MEMBER(msg, i, msg));
373 			if (ssh_msg_send(ctxt->pam_csock,
374 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
375 				goto fail;
376 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
377 				goto fail;
378 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
379 				goto fail;
380 			reply[i].resp = buffer_get_string(&buffer, NULL);
381 			break;
382 		case PAM_PROMPT_ECHO_ON:
383 			buffer_put_cstring(&buffer,
384 			    PAM_MSG_MEMBER(msg, i, msg));
385 			if (ssh_msg_send(ctxt->pam_csock,
386 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
387 				goto fail;
388 			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
389 				goto fail;
390 			if (buffer_get_char(&buffer) != PAM_AUTHTOK)
391 				goto fail;
392 			reply[i].resp = buffer_get_string(&buffer, NULL);
393 			break;
394 		case PAM_ERROR_MSG:
395 			buffer_put_cstring(&buffer,
396 			    PAM_MSG_MEMBER(msg, i, msg));
397 			if (ssh_msg_send(ctxt->pam_csock,
398 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
399 				goto fail;
400 			break;
401 		case PAM_TEXT_INFO:
402 			buffer_put_cstring(&buffer,
403 			    PAM_MSG_MEMBER(msg, i, msg));
404 			if (ssh_msg_send(ctxt->pam_csock,
405 			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
406 				goto fail;
407 			break;
408 		default:
409 			goto fail;
410 		}
411 		buffer_clear(&buffer);
412 	}
413 	buffer_free(&buffer);
414 	*resp = reply;
415 	return (PAM_SUCCESS);
416 
417  fail:
418 	for(i = 0; i < n; i++) {
419 		free(reply[i].resp);
420 	}
421 	free(reply);
422 	buffer_free(&buffer);
423 	return (PAM_CONV_ERR);
424 }
425 
426 /*
427  * Authentication thread.
428  */
429 static void *
430 sshpam_thread(void *ctxtp)
431 {
432 	struct pam_ctxt *ctxt = ctxtp;
433 	Buffer buffer;
434 	struct pam_conv sshpam_conv;
435 	int flags = (options.permit_empty_passwd == 0 ?
436 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
437 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
438 	extern char **environ;
439 	char **env_from_pam;
440 	u_int i;
441 	const char *pam_user;
442 	const char **ptr_pam_user = &pam_user;
443 	char *tz = getenv("TZ");
444 
445 	sshpam_err = pam_get_item(sshpam_handle, PAM_USER,
446 	    (sshpam_const void **)ptr_pam_user);
447 	if (sshpam_err != PAM_SUCCESS)
448 		goto auth_fail;
449 
450 	environ[0] = NULL;
451 	if (tz != NULL)
452 		if (setenv("TZ", tz, 1) == -1)
453 			error("PAM: could not set TZ environment: %s",
454 			    strerror(errno));
455 
456 	if (sshpam_authctxt != NULL) {
457 		setproctitle("%s [pam]",
458 		    sshpam_authctxt->valid ? pam_user : "unknown");
459 	}
460 #endif
461 
462 	sshpam_conv.conv = sshpam_thread_conv;
463 	sshpam_conv.appdata_ptr = ctxt;
464 
465 	if (sshpam_authctxt == NULL)
466 		fatal("%s: PAM authctxt not initialized", __func__);
467 
468 	buffer_init(&buffer);
469 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
470 	    (const void *)&sshpam_conv);
471 	if (sshpam_err != PAM_SUCCESS)
472 		goto auth_fail;
473 	sshpam_err = pam_authenticate(sshpam_handle, flags);
474 	if (sshpam_err != PAM_SUCCESS)
475 		goto auth_fail;
476 
477 	if (compat20) {
478 		if (!do_pam_account()) {
479 			sshpam_err = PAM_ACCT_EXPIRED;
480 			goto auth_fail;
481 		}
482 		if (sshpam_authctxt->force_pwchange) {
483 			sshpam_err = pam_chauthtok(sshpam_handle,
484 			    PAM_CHANGE_EXPIRED_AUTHTOK);
485 			if (sshpam_err != PAM_SUCCESS)
486 				goto auth_fail;
487 			sshpam_password_change_required(0);
488 		}
489 	}
490 
491 	buffer_put_cstring(&buffer, "OK");
492 
493 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
494 	/* Export variables set by do_pam_account */
495 	buffer_put_int(&buffer, sshpam_account_status);
496 	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);
497 
498 	/* Export any environment strings set in child */
499 	for(i = 0; environ[i] != NULL; i++)
500 		; /* Count */
501 	buffer_put_int(&buffer, i);
502 	for(i = 0; environ[i] != NULL; i++)
503 		buffer_put_cstring(&buffer, environ[i]);
504 
505 	/* Export any environment strings set by PAM in child */
506 	env_from_pam = pam_getenvlist(sshpam_handle);
507 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
508 		; /* Count */
509 	buffer_put_int(&buffer, i);
510 	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
511 		buffer_put_cstring(&buffer, env_from_pam[i]);
512 #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
513 
514 	/* XXX - can't do much about an error here */
515 	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
516 	buffer_free(&buffer);
517 	pthread_exit(NULL);
518 
519  auth_fail:
520 	buffer_put_cstring(&buffer,
521 	    pam_strerror(sshpam_handle, sshpam_err));
522 	/* XXX - can't do much about an error here */
523 	if (sshpam_err == PAM_ACCT_EXPIRED)
524 		ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer);
525 	else
526 		ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
527 	buffer_free(&buffer);
528 	pthread_exit(NULL);
529 
530 	return (NULL); /* Avoid warning for non-pthread case */
531 }
532 
533 void
534 sshpam_thread_cleanup(void)
535 {
536 	struct pam_ctxt *ctxt = cleanup_ctxt;
537 
538 	debug3("PAM: %s entering", __func__);
539 	if (ctxt != NULL && ctxt->pam_thread != 0) {
540 		pthread_cancel(ctxt->pam_thread);
541 		pthread_join(ctxt->pam_thread, NULL);
542 		close(ctxt->pam_psock);
543 		close(ctxt->pam_csock);
544 		memset(ctxt, 0, sizeof(*ctxt));
545 		cleanup_ctxt = NULL;
546 	}
547 }
548 
549 static int
550 sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
551     struct pam_response **resp, void *data)
552 {
553 	debug3("PAM: %s entering, %d messages", __func__, n);
554 	return (PAM_CONV_ERR);
555 }
556 
557 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
558 
559 static int
560 sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
561     struct pam_response **resp, void *data)
562 {
563 	struct pam_response *reply;
564 	int i;
565 	size_t len;
566 
567 	debug3("PAM: %s called with %d messages", __func__, n);
568 	*resp = NULL;
569 
570 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
571 		return (PAM_CONV_ERR);
572 
573 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
574 		return (PAM_CONV_ERR);
575 
576 	for (i = 0; i < n; ++i) {
577 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
578 		case PAM_ERROR_MSG:
579 		case PAM_TEXT_INFO:
580 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
581 			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);
582 			buffer_append(&loginmsg, "\n", 1 );
583 			reply[i].resp_retcode = PAM_SUCCESS;
584 			break;
585 		default:
586 			goto fail;
587 		}
588 	}
589 	*resp = reply;
590 	return (PAM_SUCCESS);
591 
592  fail:
593 	for(i = 0; i < n; i++) {
594 		free(reply[i].resp);
595 	}
596 	free(reply);
597 	return (PAM_CONV_ERR);
598 }
599 
600 static struct pam_conv store_conv = { sshpam_store_conv, NULL };
601 
602 void
603 sshpam_cleanup(void)
604 {
605 	if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor()))
606 		return;
607 	debug("PAM: cleanup");
608 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
609 	if (sshpam_session_open) {
610 		debug("PAM: closing session");
611 		pam_close_session(sshpam_handle, PAM_SILENT);
612 		sshpam_session_open = 0;
613 	}
614 	if (sshpam_cred_established) {
615 		debug("PAM: deleting credentials");
616 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
617 		sshpam_cred_established = 0;
618 	}
619 	sshpam_authenticated = 0;
620 	pam_end(sshpam_handle, sshpam_err);
621 	sshpam_handle = NULL;
622 }
623 
624 static int
625 sshpam_init(Authctxt *authctxt)
626 {
627 	extern char *__progname;
628 	const char *pam_rhost, *pam_user, *user = authctxt->user;
629 	const char **ptr_pam_user = &pam_user;
630 
631 	if (sshpam_handle != NULL) {
632 		/* We already have a PAM context; check if the user matches */
633 		sshpam_err = pam_get_item(sshpam_handle,
634 		    PAM_USER, (sshpam_const void **)ptr_pam_user);
635 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
636 			return (0);
637 		pam_end(sshpam_handle, sshpam_err);
638 		sshpam_handle = NULL;
639 	}
640 	debug("PAM: initializing for \"%s\"", user);
641 	sshpam_err =
642 	    pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
643 	sshpam_authctxt = authctxt;
644 
645 	if (sshpam_err != PAM_SUCCESS) {
646 		pam_end(sshpam_handle, sshpam_err);
647 		sshpam_handle = NULL;
648 		return (-1);
649 	}
650 	pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
651 	debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
652 	sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
653 	if (sshpam_err != PAM_SUCCESS) {
654 		pam_end(sshpam_handle, sshpam_err);
655 		sshpam_handle = NULL;
656 		return (-1);
657 	}
658 #ifdef PAM_TTY_KLUDGE
659 	/*
660 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
661 	 * sshd doesn't set the tty until too late in the auth process and
662 	 * may not even set one (for tty-less connections)
663 	 */
664 	debug("PAM: setting PAM_TTY to \"ssh\"");
665 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
666 	if (sshpam_err != PAM_SUCCESS) {
667 		pam_end(sshpam_handle, sshpam_err);
668 		sshpam_handle = NULL;
669 		return (-1);
670 	}
671 #endif
672 	return (0);
673 }
674 
675 static void *
676 sshpam_init_ctx(Authctxt *authctxt)
677 {
678 	struct pam_ctxt *ctxt;
679 	int socks[2];
680 
681 	debug3("PAM: %s entering", __func__);
682 	/*
683 	 * Refuse to start if we don't have PAM enabled or do_pam_account
684 	 * has previously failed.
685 	 */
686 	if (!options.use_pam || sshpam_account_status == 0)
687 		return NULL;
688 
689 	/* Initialize PAM */
690 	if (sshpam_init(authctxt) == -1) {
691 		error("PAM: initialization failed");
692 		return (NULL);
693 	}
694 
695 	ctxt = xcalloc(1, sizeof *ctxt);
696 
697 	/* Start the authentication thread */
698 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
699 		error("PAM: failed create sockets: %s", strerror(errno));
700 		free(ctxt);
701 		return (NULL);
702 	}
703 	ctxt->pam_psock = socks[0];
704 	ctxt->pam_csock = socks[1];
705 	if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
706 		error("PAM: failed to start authentication thread: %s",
707 		    strerror(errno));
708 		close(socks[0]);
709 		close(socks[1]);
710 		free(ctxt);
711 		return (NULL);
712 	}
713 	cleanup_ctxt = ctxt;
714 	return (ctxt);
715 }
716 
717 static int
718 sshpam_query(void *ctx, char **name, char **info,
719     u_int *num, char ***prompts, u_int **echo_on)
720 {
721 	Buffer buffer;
722 	struct pam_ctxt *ctxt = ctx;
723 	size_t plen;
724 	u_char type;
725 	char *msg;
726 	size_t len, mlen;
727 
728 	debug3("PAM: %s entering", __func__);
729 	buffer_init(&buffer);
730 	*name = xstrdup("");
731 	*info = xstrdup("");
732 	*prompts = xmalloc(sizeof(char *));
733 	**prompts = NULL;
734 	plen = 0;
735 	*echo_on = xmalloc(sizeof(u_int));
736 	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
737 		type = buffer_get_char(&buffer);
738 		msg = buffer_get_string(&buffer, NULL);
739 		mlen = strlen(msg);
740 		switch (type) {
741 		case PAM_PROMPT_ECHO_ON:
742 		case PAM_PROMPT_ECHO_OFF:
743 			*num = 1;
744 			len = plen + mlen + 1;
745 			**prompts = xreallocarray(**prompts, 1, len);
746 			strlcpy(**prompts + plen, msg, len - plen);
747 			plen += mlen;
748 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
749 			free(msg);
750 			return (0);
751 		case PAM_ERROR_MSG:
752 		case PAM_TEXT_INFO:
753 			/* accumulate messages */
754 			len = plen + mlen + 2;
755 			**prompts = xreallocarray(**prompts, 1, len);
756 			strlcpy(**prompts + plen, msg, len - plen);
757 			plen += mlen;
758 			strlcat(**prompts + plen, "\n", len - plen);
759 			plen++;
760 			free(msg);
761 			break;
762 		case PAM_ACCT_EXPIRED:
763 			sshpam_account_status = 0;
764 			/* FALLTHROUGH */
765 		case PAM_AUTH_ERR:
766 			debug3("PAM: %s", pam_strerror(sshpam_handle, type));
767 			if (**prompts != NULL && strlen(**prompts) != 0) {
768 				*info = **prompts;
769 				**prompts = NULL;
770 				*num = 0;
771 				**echo_on = 0;
772 				ctxt->pam_done = -1;
773 				free(msg);
774 				return 0;
775 			}
776 			/* FALLTHROUGH */
777 		case PAM_SUCCESS:
778 			if (**prompts != NULL) {
779 				/* drain any accumulated messages */
780 				debug("PAM: %s", **prompts);
781 				buffer_append(&loginmsg, **prompts,
782 				    strlen(**prompts));
783 				free(**prompts);
784 				**prompts = NULL;
785 			}
786 			if (type == PAM_SUCCESS) {
787 				if (!sshpam_authctxt->valid ||
788 				    (sshpam_authctxt->pw->pw_uid == 0 &&
789 				    options.permit_root_login != PERMIT_YES))
790 					fatal("Internal error: PAM auth "
791 					    "succeeded when it should have "
792 					    "failed");
793 				import_environments(&buffer);
794 				*num = 0;
795 				**echo_on = 0;
796 				ctxt->pam_done = 1;
797 				free(msg);
798 				return (0);
799 			}
800 #ifdef USE_BLACKLIST
801 			blacklist_notify(1);
802 #endif
803 			error("PAM: %s for %s%.100s from %.100s", msg,
804 			    sshpam_authctxt->valid ? "" : "illegal user ",
805 			    sshpam_authctxt->user,
806 			    get_remote_name_or_ip(utmp_len, options.use_dns));
807 			/* FALLTHROUGH */
808 		default:
809 			*num = 0;
810 			**echo_on = 0;
811 			free(msg);
812 			ctxt->pam_done = -1;
813 			return (-1);
814 		}
815 	}
816 	return (-1);
817 }
818 
819 /* XXX - see also comment in auth-chall.c:verify_response */
820 static int
821 sshpam_respond(void *ctx, u_int num, char **resp)
822 {
823 	Buffer buffer;
824 	struct pam_ctxt *ctxt = ctx;
825 
826 	debug2("PAM: %s entering, %u responses", __func__, num);
827 	switch (ctxt->pam_done) {
828 	case 1:
829 		sshpam_authenticated = 1;
830 		return (0);
831 	case 0:
832 		break;
833 	default:
834 		return (-1);
835 	}
836 	if (num != 1) {
837 		error("PAM: expected one response, got %u", num);
838 		return (-1);
839 	}
840 	buffer_init(&buffer);
841 	if (sshpam_authctxt->valid &&
842 	    (sshpam_authctxt->pw->pw_uid != 0 ||
843 	    options.permit_root_login == PERMIT_YES))
844 		buffer_put_cstring(&buffer, *resp);
845 	else
846 		buffer_put_cstring(&buffer, badpw);
847 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
848 		buffer_free(&buffer);
849 		return (-1);
850 	}
851 	buffer_free(&buffer);
852 	return (1);
853 }
854 
855 static void
856 sshpam_free_ctx(void *ctxtp)
857 {
858 	struct pam_ctxt *ctxt = ctxtp;
859 
860 	debug3("PAM: %s entering", __func__);
861 	sshpam_thread_cleanup();
862 	free(ctxt);
863 	/*
864 	 * We don't call sshpam_cleanup() here because we may need the PAM
865 	 * handle at a later stage, e.g. when setting up a session.  It's
866 	 * still on the cleanup list, so pam_end() *will* be called before
867 	 * the server process terminates.
868 	 */
869 }
870 
871 KbdintDevice sshpam_device = {
872 	"pam",
873 	sshpam_init_ctx,
874 	sshpam_query,
875 	sshpam_respond,
876 	sshpam_free_ctx
877 };
878 
879 KbdintDevice mm_sshpam_device = {
880 	"pam",
881 	mm_sshpam_init_ctx,
882 	mm_sshpam_query,
883 	mm_sshpam_respond,
884 	mm_sshpam_free_ctx
885 };
886 
887 /*
888  * This replaces auth-pam.c
889  */
890 void
891 start_pam(Authctxt *authctxt)
892 {
893 	if (!options.use_pam)
894 		fatal("PAM: initialisation requested when UsePAM=no");
895 
896 	if (sshpam_init(authctxt) == -1)
897 		fatal("PAM: initialisation failed");
898 }
899 
900 void
901 finish_pam(void)
902 {
903 	sshpam_cleanup();
904 }
905 
906 u_int
907 do_pam_account(void)
908 {
909 	debug("%s: called", __func__);
910 	if (sshpam_account_status != -1)
911 		return (sshpam_account_status);
912 
913 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
914 	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
915 	    pam_strerror(sshpam_handle, sshpam_err));
916 
917 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
918 		sshpam_account_status = 0;
919 		return (sshpam_account_status);
920 	}
921 
922 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
923 		sshpam_password_change_required(1);
924 
925 	sshpam_account_status = 1;
926 	return (sshpam_account_status);
927 }
928 
929 void
930 do_pam_set_tty(const char *tty)
931 {
932 	if (tty != NULL) {
933 		debug("PAM: setting PAM_TTY to \"%s\"", tty);
934 		sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
935 		if (sshpam_err != PAM_SUCCESS)
936 			fatal("PAM: failed to set PAM_TTY: %s",
937 			    pam_strerror(sshpam_handle, sshpam_err));
938 	}
939 }
940 
941 void
942 do_pam_setcred(int init)
943 {
944 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
945 	    (const void *)&store_conv);
946 	if (sshpam_err != PAM_SUCCESS)
947 		fatal("PAM: failed to set PAM_CONV: %s",
948 		    pam_strerror(sshpam_handle, sshpam_err));
949 	if (init) {
950 		debug("PAM: establishing credentials");
951 		sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
952 	} else {
953 		debug("PAM: reinitializing credentials");
954 		sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
955 	}
956 	if (sshpam_err == PAM_SUCCESS) {
957 		sshpam_cred_established = 1;
958 		return;
959 	}
960 	if (sshpam_authenticated)
961 		fatal("PAM: pam_setcred(): %s",
962 		    pam_strerror(sshpam_handle, sshpam_err));
963 	else
964 		debug("PAM: pam_setcred(): %s",
965 		    pam_strerror(sshpam_handle, sshpam_err));
966 }
967 
968 static int
969 sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
970     struct pam_response **resp, void *data)
971 {
972 	char input[PAM_MAX_MSG_SIZE];
973 	struct pam_response *reply;
974 	int i;
975 
976 	debug3("PAM: %s called with %d messages", __func__, n);
977 
978 	*resp = NULL;
979 
980 	if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO))
981 		return (PAM_CONV_ERR);
982 
983 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
984 		return (PAM_CONV_ERR);
985 
986 	for (i = 0; i < n; ++i) {
987 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
988 		case PAM_PROMPT_ECHO_OFF:
989 			reply[i].resp =
990 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
991 			    RP_ALLOW_STDIN);
992 			reply[i].resp_retcode = PAM_SUCCESS;
993 			break;
994 		case PAM_PROMPT_ECHO_ON:
995 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
996 			if (fgets(input, sizeof input, stdin) == NULL)
997 				input[0] = '\0';
998 			if ((reply[i].resp = strdup(input)) == NULL)
999 				goto fail;
1000 			reply[i].resp_retcode = PAM_SUCCESS;
1001 			break;
1002 		case PAM_ERROR_MSG:
1003 		case PAM_TEXT_INFO:
1004 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
1005 			reply[i].resp_retcode = PAM_SUCCESS;
1006 			break;
1007 		default:
1008 			goto fail;
1009 		}
1010 	}
1011 	*resp = reply;
1012 	return (PAM_SUCCESS);
1013 
1014  fail:
1015 	for(i = 0; i < n; i++) {
1016 		free(reply[i].resp);
1017 	}
1018 	free(reply);
1019 	return (PAM_CONV_ERR);
1020 }
1021 
1022 static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
1023 
1024 /*
1025  * XXX this should be done in the authentication phase, but ssh1 doesn't
1026  * support that
1027  */
1028 void
1029 do_pam_chauthtok(void)
1030 {
1031 	if (use_privsep)
1032 		fatal("Password expired (unable to change with privsep)");
1033 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1034 	    (const void *)&tty_conv);
1035 	if (sshpam_err != PAM_SUCCESS)
1036 		fatal("PAM: failed to set PAM_CONV: %s",
1037 		    pam_strerror(sshpam_handle, sshpam_err));
1038 	debug("PAM: changing password");
1039 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
1040 	if (sshpam_err != PAM_SUCCESS)
1041 		fatal("PAM: pam_chauthtok(): %s",
1042 		    pam_strerror(sshpam_handle, sshpam_err));
1043 }
1044 
1045 void
1046 do_pam_session(void)
1047 {
1048 	debug3("PAM: opening session");
1049 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1050 	    (const void *)&store_conv);
1051 	if (sshpam_err != PAM_SUCCESS)
1052 		fatal("PAM: failed to set PAM_CONV: %s",
1053 		    pam_strerror(sshpam_handle, sshpam_err));
1054 	sshpam_err = pam_open_session(sshpam_handle, 0);
1055 	if (sshpam_err == PAM_SUCCESS)
1056 		sshpam_session_open = 1;
1057 	else {
1058 		sshpam_session_open = 0;
1059 		disable_forwarding();
1060 		error("PAM: pam_open_session(): %s",
1061 		    pam_strerror(sshpam_handle, sshpam_err));
1062 	}
1063 
1064 }
1065 
1066 int
1067 is_pam_session_open(void)
1068 {
1069 	return sshpam_session_open;
1070 }
1071 
1072 /*
1073  * Set a PAM environment string. We need to do this so that the session
1074  * modules can handle things like Kerberos/GSI credentials that appear
1075  * during the ssh authentication process.
1076  */
1077 int
1078 do_pam_putenv(char *name, char *value)
1079 {
1080 	int ret = 1;
1081 #ifdef HAVE_PAM_PUTENV
1082 	char *compound;
1083 	size_t len;
1084 
1085 	len = strlen(name) + strlen(value) + 2;
1086 	compound = xmalloc(len);
1087 
1088 	snprintf(compound, len, "%s=%s", name, value);
1089 	ret = pam_putenv(sshpam_handle, compound);
1090 	free(compound);
1091 #endif
1092 
1093 	return (ret);
1094 }
1095 
1096 char **
1097 fetch_pam_child_environment(void)
1098 {
1099 	return sshpam_env;
1100 }
1101 
1102 char **
1103 fetch_pam_environment(void)
1104 {
1105 	return (pam_getenvlist(sshpam_handle));
1106 }
1107 
1108 void
1109 free_pam_environment(char **env)
1110 {
1111 	char **envp;
1112 
1113 	if (env == NULL)
1114 		return;
1115 
1116 	for (envp = env; *envp; envp++)
1117 		free(*envp);
1118 	free(env);
1119 }
1120 
1121 /*
1122  * "Blind" conversation function for password authentication.  Assumes that
1123  * echo-off prompts are for the password and stores messages for later
1124  * display.
1125  */
1126 static int
1127 sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
1128     struct pam_response **resp, void *data)
1129 {
1130 	struct pam_response *reply;
1131 	int i;
1132 	size_t len;
1133 
1134 	debug3("PAM: %s called with %d messages", __func__, n);
1135 
1136 	*resp = NULL;
1137 
1138 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
1139 		return (PAM_CONV_ERR);
1140 
1141 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
1142 		return (PAM_CONV_ERR);
1143 
1144 	for (i = 0; i < n; ++i) {
1145 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
1146 		case PAM_PROMPT_ECHO_OFF:
1147 			if (sshpam_password == NULL)
1148 				goto fail;
1149 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
1150 				goto fail;
1151 			reply[i].resp_retcode = PAM_SUCCESS;
1152 			break;
1153 		case PAM_ERROR_MSG:
1154 		case PAM_TEXT_INFO:
1155 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
1156 			if (len > 0) {
1157 				buffer_append(&loginmsg,
1158 				    PAM_MSG_MEMBER(msg, i, msg), len);
1159 				buffer_append(&loginmsg, "\n", 1);
1160 			}
1161 			if ((reply[i].resp = strdup("")) == NULL)
1162 				goto fail;
1163 			reply[i].resp_retcode = PAM_SUCCESS;
1164 			break;
1165 		default:
1166 			goto fail;
1167 		}
1168 	}
1169 	*resp = reply;
1170 	return (PAM_SUCCESS);
1171 
1172  fail:
1173 	for(i = 0; i < n; i++) {
1174 		free(reply[i].resp);
1175 	}
1176 	free(reply);
1177 	return (PAM_CONV_ERR);
1178 }
1179 
1180 static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
1181 
1182 /*
1183  * Attempt password authentication via PAM
1184  */
1185 int
1186 sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1187 {
1188 	int flags = (options.permit_empty_passwd == 0 ?
1189 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
1190 
1191 	if (!options.use_pam || sshpam_handle == NULL)
1192 		fatal("PAM: %s called when PAM disabled or failed to "
1193 		    "initialise.", __func__);
1194 
1195 	sshpam_password = password;
1196 	sshpam_authctxt = authctxt;
1197 
1198 	/*
1199 	 * If the user logging in is invalid, or is root but is not permitted
1200 	 * by PermitRootLogin, use an invalid password to prevent leaking
1201 	 * information via timing (eg if the PAM config has a delay on fail).
1202 	 */
1203 	if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
1204 	    options.permit_root_login != PERMIT_YES))
1205 		sshpam_password = badpw;
1206 
1207 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1208 	    (const void *)&passwd_conv);
1209 	if (sshpam_err != PAM_SUCCESS)
1210 		fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
1211 		    pam_strerror(sshpam_handle, sshpam_err));
1212 
1213 	sshpam_err = pam_authenticate(sshpam_handle, flags);
1214 	sshpam_password = NULL;
1215 	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
1216 		debug("PAM: password authentication accepted for %.100s",
1217 		    authctxt->user);
1218 		return 1;
1219 	} else {
1220 		debug("PAM: password authentication failed for %.100s: %s",
1221 		    authctxt->valid ? authctxt->user : "an illegal user",
1222 		    pam_strerror(sshpam_handle, sshpam_err));
1223 		return 0;
1224 	}
1225 }
1226 #endif /* USE_PAM */
1227