xref: /dragonfly/crypto/openssh/auth-pam.c (revision ba1276ac)
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 <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 #ifdef USE_PAM
64 #if defined(HAVE_SECURITY_PAM_APPL_H)
65 #include <security/pam_appl.h>
66 #elif defined (HAVE_PAM_PAM_APPL_H)
67 #include <pam/pam_appl.h>
68 #endif
69 
70 /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
71 #ifdef PAM_SUN_CODEBASE
72 # define sshpam_const		/* Solaris, HP-UX, SunOS */
73 #else
74 # define sshpam_const	const	/* LinuxPAM, OpenPAM, AIX */
75 #endif
76 
77 /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
78 #ifdef PAM_SUN_CODEBASE
79 # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
80 #else
81 # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
82 #endif
83 
84 #include "xmalloc.h"
85 #include "sshbuf.h"
86 #include "ssherr.h"
87 #include "hostfile.h"
88 #include "auth.h"
89 #include "auth-pam.h"
90 #include "canohost.h"
91 #include "log.h"
92 #include "msg.h"
93 #include "packet.h"
94 #include "misc.h"
95 #include "servconf.h"
96 #include "ssh2.h"
97 #include "auth-options.h"
98 #include "misc.h"
99 #ifdef GSSAPI
100 #include "ssh-gss.h"
101 #endif
102 #include "monitor_wrap.h"
103 #include "srclimit.h"
104 
105 extern ServerOptions options;
106 extern struct sshbuf *loginmsg;
107 extern u_int utmp_len;
108 
109 /* so we don't silently change behaviour */
110 #ifdef USE_POSIX_THREADS
111 # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
112 #endif
113 
114 /*
115  * Formerly known as USE_POSIX_THREADS, using this is completely unsupported
116  * and generally a bad idea.  Use at own risk and do not expect support if
117  * this breaks.
118  */
119 #ifdef UNSUPPORTED_POSIX_THREADS_HACK
120 #include <pthread.h>
121 /*
122  * Avoid namespace clash when *not* using pthreads for systems *with*
123  * pthreads, which unconditionally define pthread_t via sys/types.h
124  * (e.g. Linux)
125  */
126 typedef pthread_t sp_pthread_t;
127 #else
128 typedef pid_t sp_pthread_t;
129 #define pthread_exit	fake_pthread_exit
130 #define pthread_create	fake_pthread_create
131 #define pthread_cancel	fake_pthread_cancel
132 #define pthread_join	fake_pthread_join
133 #endif
134 
135 struct pam_ctxt {
136 	sp_pthread_t	 pam_thread;
137 	int		 pam_psock;
138 	int		 pam_csock;
139 	int		 pam_done;
140 };
141 
142 static void sshpam_free_ctx(void *);
143 static struct pam_ctxt *cleanup_ctxt;
144 
145 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
146 /*
147  * Simulate threads with processes.
148  */
149 
150 static int sshpam_thread_status = -1;
151 static sshsig_t sshpam_oldsig;
152 
153 static void
sshpam_sigchld_handler(int sig)154 sshpam_sigchld_handler(int sig)
155 {
156 	ssh_signal(SIGCHLD, SIG_DFL);
157 	if (cleanup_ctxt == NULL)
158 		return;	/* handler called after PAM cleanup, shouldn't happen */
159 	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
160 	    <= 0) {
161 		/* PAM thread has not exitted, privsep slave must have */
162 		kill(cleanup_ctxt->pam_thread, SIGTERM);
163 		while (waitpid(cleanup_ctxt->pam_thread,
164 		    &sshpam_thread_status, 0) == -1) {
165 			if (errno == EINTR)
166 				continue;
167 			return;
168 		}
169 	}
170 	if (sshpam_thread_status == -1)
171 		return;
172 	if (WIFSIGNALED(sshpam_thread_status)) {
173 		if (signal_is_crash(WTERMSIG(sshpam_thread_status)))
174 			_exit(EXIT_CHILD_CRASH);
175 	} else if (!WIFEXITED(sshpam_thread_status))
176 		_exit(EXIT_CHILD_CRASH);
177 }
178 
179 /* ARGSUSED */
180 static void
pthread_exit(void * value)181 pthread_exit(void *value)
182 {
183 	_exit(0);
184 }
185 
186 /* ARGSUSED */
187 static int
pthread_create(sp_pthread_t * thread,const void * attr,void * (* thread_start)(void *),void * arg)188 pthread_create(sp_pthread_t *thread, const void *attr,
189     void *(*thread_start)(void *), void *arg)
190 {
191 	pid_t pid;
192 	struct pam_ctxt *ctx = arg;
193 
194 	sshpam_thread_status = -1;
195 	switch ((pid = fork())) {
196 	case -1:
197 		error("fork(): %s", strerror(errno));
198 		return errno;
199 	case 0:
200 		close(ctx->pam_psock);
201 		ctx->pam_psock = -1;
202 		thread_start(arg);
203 		_exit(1);
204 	default:
205 		*thread = pid;
206 		close(ctx->pam_csock);
207 		ctx->pam_csock = -1;
208 		sshpam_oldsig = ssh_signal(SIGCHLD, sshpam_sigchld_handler);
209 		return (0);
210 	}
211 }
212 
213 static int
pthread_cancel(sp_pthread_t thread)214 pthread_cancel(sp_pthread_t thread)
215 {
216 	ssh_signal(SIGCHLD, sshpam_oldsig);
217 	return (kill(thread, SIGTERM));
218 }
219 
220 /* ARGSUSED */
221 static int
pthread_join(sp_pthread_t thread,void ** value)222 pthread_join(sp_pthread_t thread, void **value)
223 {
224 	int status;
225 
226 	if (sshpam_thread_status != -1)
227 		return (sshpam_thread_status);
228 	ssh_signal(SIGCHLD, sshpam_oldsig);
229 	while (waitpid(thread, &status, 0) == -1) {
230 		if (errno == EINTR)
231 			continue;
232 		fatal("%s: waitpid: %s", __func__, strerror(errno));
233 	}
234 	return (status);
235 }
236 #endif
237 
238 
239 static pam_handle_t *sshpam_handle = NULL;
240 static int sshpam_err = 0;
241 static int sshpam_authenticated = 0;
242 static int sshpam_session_open = 0;
243 static int sshpam_cred_established = 0;
244 static int sshpam_account_status = -1;
245 static int sshpam_maxtries_reached = 0;
246 static char **sshpam_env = NULL;
247 static Authctxt *sshpam_authctxt = NULL;
248 static const char *sshpam_password = NULL;
249 static char *sshpam_rhost = NULL;
250 static char *sshpam_laddr = NULL;
251 
252 /* Some PAM implementations don't implement this */
253 #ifndef HAVE_PAM_GETENVLIST
254 static char **
pam_getenvlist(pam_handle_t * pamh)255 pam_getenvlist(pam_handle_t *pamh)
256 {
257 	/*
258 	 * XXX - If necessary, we can still support environment passing
259 	 * for platforms without pam_getenvlist by searching for known
260 	 * env vars (e.g. KRB5CCNAME) from the PAM environment.
261 	 */
262 	 return NULL;
263 }
264 #endif
265 
266 #ifndef HAVE_PAM_PUTENV
267 static int
pam_putenv(pam_handle_t * pamh,const char * name_value)268 pam_putenv(pam_handle_t *pamh, const char *name_value)
269 {
270 	return PAM_SUCCESS;
271 }
272 #endif /* HAVE_PAM_PUTENV */
273 
274 /*
275  * Some platforms, notably Solaris, do not enforce password complexity
276  * rules during pam_chauthtok() if the real uid of the calling process
277  * is 0, on the assumption that it's being called by "passwd" run by root.
278  * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
279  * the right thing.
280  */
281 #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
282 static int
sshpam_chauthtok_ruid(pam_handle_t * pamh,int flags)283 sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
284 {
285 	int result;
286 
287 	if (sshpam_authctxt == NULL)
288 		fatal("PAM: sshpam_authctxt not initialized");
289 	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
290 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
291 	result = pam_chauthtok(pamh, flags);
292 	if (setreuid(0, -1) == -1)
293 		fatal("%s: setreuid failed: %s", __func__, strerror(errno));
294 	return result;
295 }
296 # define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))
297 #endif
298 
299 static void
sshpam_password_change_required(int reqd)300 sshpam_password_change_required(int reqd)
301 {
302 	extern struct sshauthopt *auth_opts;
303 	static int saved_port, saved_agent, saved_x11;
304 
305 	debug3("%s %d", __func__, reqd);
306 	if (sshpam_authctxt == NULL)
307 		fatal("%s: PAM authctxt not initialized", __func__);
308 	sshpam_authctxt->force_pwchange = reqd;
309 	if (reqd) {
310 		saved_port = auth_opts->permit_port_forwarding_flag;
311 		saved_agent = auth_opts->permit_agent_forwarding_flag;
312 		saved_x11 = auth_opts->permit_x11_forwarding_flag;
313 		auth_opts->permit_port_forwarding_flag = 0;
314 		auth_opts->permit_agent_forwarding_flag = 0;
315 		auth_opts->permit_x11_forwarding_flag = 0;
316 	} else {
317 		if (saved_port)
318 			auth_opts->permit_port_forwarding_flag = saved_port;
319 		if (saved_agent)
320 			auth_opts->permit_agent_forwarding_flag = saved_agent;
321 		if (saved_x11)
322 			auth_opts->permit_x11_forwarding_flag = saved_x11;
323 	}
324 }
325 
326 /* Import regular and PAM environment from subprocess */
327 static void
import_environments(struct sshbuf * b)328 import_environments(struct sshbuf *b)
329 {
330 	char *env;
331 	u_int n, i, num_env;
332 	int r;
333 
334 	debug3("PAM: %s entering", __func__);
335 
336 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
337 	/* Import variables set by do_pam_account */
338 	if ((r = sshbuf_get_u32(b, &n)) != 0)
339 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
340 	if (n > INT_MAX)
341 		fatal("%s: invalid PAM account status %u", __func__, n);
342 	sshpam_account_status = (int)n;
343 	if ((r = sshbuf_get_u32(b, &n)) != 0)
344 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
345 	sshpam_password_change_required(n != 0);
346 
347 	/* Import environment from subprocess */
348 	if ((r = sshbuf_get_u32(b, &num_env)) != 0)
349 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
350 	if (num_env > 1024) {
351 		fatal_f("received %u environment variables, expected <= 1024",
352 		    num_env);
353 	}
354 	sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env));
355 	debug3("PAM: num env strings %u", num_env);
356 	for(i = 0; i < num_env; i++) {
357 		if ((r = sshbuf_get_cstring(b, &(sshpam_env[i]), NULL)) != 0)
358 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
359 	}
360 	sshpam_env[num_env] = NULL;
361 
362 	/* Import PAM environment from subprocess */
363 	if ((r = sshbuf_get_u32(b, &num_env)) != 0)
364 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
365 	if (num_env > 1024) {
366 		fatal_f("received %u PAM env variables, expected <= 1024",
367 		    num_env);
368 	}
369 	debug("PAM: num PAM env strings %u", num_env);
370 	for (i = 0; i < num_env; i++) {
371 		if ((r = sshbuf_get_cstring(b, &env, NULL)) != 0)
372 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
373 		/* Errors are not fatal here */
374 		if ((r = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {
375 			error("PAM: pam_putenv: %s",
376 			    pam_strerror(sshpam_handle, r));
377 		}
378 		/*
379 		 * XXX this possibly leaks env because it is not documented
380 		 * what pam_putenv() does with it. Does it copy it? Does it
381 		 * take ownweship? We don't know, so it's safest just to leak.
382 		 */
383 	}
384 #endif
385 }
386 
387 /*
388  * Conversation function for authentication thread.
389  */
390 static int
sshpam_thread_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)391 sshpam_thread_conv(int n, sshpam_const struct pam_message **msg,
392     struct pam_response **resp, void *data)
393 {
394 	struct sshbuf *buffer;
395 	struct pam_ctxt *ctxt;
396 	struct pam_response *reply;
397 	int r, i;
398 	u_char status;
399 
400 	debug3("PAM: %s entering, %d messages", __func__, n);
401 	*resp = NULL;
402 
403 	if (data == NULL) {
404 		error("PAM: conversation function passed a null context");
405 		return (PAM_CONV_ERR);
406 	}
407 	ctxt = data;
408 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
409 		return (PAM_CONV_ERR);
410 
411 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
412 		return PAM_CONV_ERR;
413 	if ((buffer = sshbuf_new()) == NULL) {
414 		free(reply);
415 		return PAM_CONV_ERR;
416 	}
417 
418 	for (i = 0; i < n; ++i) {
419 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
420 		case PAM_PROMPT_ECHO_OFF:
421 		case PAM_PROMPT_ECHO_ON:
422 			if ((r = sshbuf_put_cstring(buffer,
423 			    PAM_MSG_MEMBER(msg, i, msg))) != 0)
424 				fatal("%s: buffer error: %s",
425 				    __func__, ssh_err(r));
426 			if (ssh_msg_send(ctxt->pam_csock,
427 			    PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1)
428 				goto fail;
429 
430 			if (ssh_msg_recv(ctxt->pam_csock, buffer) == -1)
431 				goto fail;
432 			if ((r = sshbuf_get_u8(buffer, &status)) != 0)
433 				fatal("%s: buffer error: %s",
434 				    __func__, ssh_err(r));
435 			if (status != PAM_AUTHTOK)
436 				goto fail;
437 			if ((r = sshbuf_get_cstring(buffer,
438 			    &reply[i].resp, NULL)) != 0)
439 				fatal("%s: buffer error: %s",
440 				    __func__, ssh_err(r));
441 			break;
442 		case PAM_ERROR_MSG:
443 		case PAM_TEXT_INFO:
444 			if ((r = sshbuf_put_cstring(buffer,
445 			    PAM_MSG_MEMBER(msg, i, msg))) != 0)
446 				fatal("%s: buffer error: %s",
447 				    __func__, ssh_err(r));
448 			if (ssh_msg_send(ctxt->pam_csock,
449 			    PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1)
450 				goto fail;
451 			break;
452 		default:
453 			goto fail;
454 		}
455 		sshbuf_reset(buffer);
456 	}
457 	sshbuf_free(buffer);
458 	*resp = reply;
459 	return (PAM_SUCCESS);
460 
461  fail:
462 	for(i = 0; i < n; i++) {
463 		free(reply[i].resp);
464 	}
465 	free(reply);
466 	sshbuf_free(buffer);
467 	return (PAM_CONV_ERR);
468 }
469 
470 /*
471  * Authentication thread.
472  */
473 static void *
sshpam_thread(void * ctxtp)474 sshpam_thread(void *ctxtp)
475 {
476 	struct pam_ctxt *ctxt = ctxtp;
477 	struct sshbuf *buffer = NULL;
478 	struct pam_conv sshpam_conv;
479 	int r, flags = (options.permit_empty_passwd == 0 ?
480 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
481 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
482 	extern char **environ;
483 	char **env_from_pam;
484 	u_int i;
485 	const char *pam_user;
486 	const char **ptr_pam_user = &pam_user;
487 	char *tz = getenv("TZ");
488 
489 	sshpam_err = pam_get_item(sshpam_handle, PAM_USER,
490 	    (sshpam_const void **)ptr_pam_user);
491 	if (sshpam_err != PAM_SUCCESS)
492 		goto auth_fail;
493 
494 	environ[0] = NULL;
495 	if (tz != NULL)
496 		if (setenv("TZ", tz, 1) == -1)
497 			error("PAM: could not set TZ environment: %s",
498 			    strerror(errno));
499 
500 	if (sshpam_authctxt != NULL) {
501 		setproctitle("%s [pam]",
502 		    sshpam_authctxt->valid ? pam_user : "unknown");
503 	}
504 #endif
505 
506 	sshpam_conv.conv = sshpam_thread_conv;
507 	sshpam_conv.appdata_ptr = ctxt;
508 
509 	if (sshpam_authctxt == NULL)
510 		fatal("%s: PAM authctxt not initialized", __func__);
511 
512 	if ((buffer = sshbuf_new()) == NULL)
513 		fatal("%s: sshbuf_new failed", __func__);
514 
515 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
516 	    (const void *)&sshpam_conv);
517 	if (sshpam_err != PAM_SUCCESS)
518 		goto auth_fail;
519 	sshpam_err = pam_authenticate(sshpam_handle, flags);
520 	if (sshpam_err == PAM_MAXTRIES)
521 		sshpam_set_maxtries_reached(1);
522 	if (sshpam_err != PAM_SUCCESS)
523 		goto auth_fail;
524 
525 	if (!do_pam_account()) {
526 		sshpam_err = PAM_ACCT_EXPIRED;
527 		goto auth_fail;
528 	}
529 	if (sshpam_authctxt->force_pwchange) {
530 		sshpam_err = pam_chauthtok(sshpam_handle,
531 		    PAM_CHANGE_EXPIRED_AUTHTOK);
532 		if (sshpam_err != PAM_SUCCESS)
533 			goto auth_fail;
534 		sshpam_password_change_required(0);
535 	}
536 
537 	if ((r = sshbuf_put_cstring(buffer, "OK")) != 0)
538 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
539 
540 #ifndef UNSUPPORTED_POSIX_THREADS_HACK
541 	/* Export variables set by do_pam_account */
542 	if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 ||
543 	    (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0)
544 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
545 
546 	/* Export any environment strings set in child */
547 	for (i = 0; environ[i] != NULL; i++) {
548 		/* Count */
549 		if (i > INT_MAX)
550 			fatal("%s: too many environment strings", __func__);
551 	}
552 	if ((r = sshbuf_put_u32(buffer, i)) != 0)
553 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
554 	for (i = 0; environ[i] != NULL; i++) {
555 		if ((r = sshbuf_put_cstring(buffer, environ[i])) != 0)
556 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
557 	}
558 	/* Export any environment strings set by PAM in child */
559 	env_from_pam = pam_getenvlist(sshpam_handle);
560 	for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) {
561 		/* Count */
562 		if (i > INT_MAX)
563 			fatal("%s: too many PAM environment strings", __func__);
564 	}
565 	if ((r = sshbuf_put_u32(buffer, i)) != 0)
566 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
567 	for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) {
568 		if ((r = sshbuf_put_cstring(buffer, env_from_pam[i])) != 0)
569 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
570 	}
571 #endif /* UNSUPPORTED_POSIX_THREADS_HACK */
572 
573 	/* XXX - can't do much about an error here */
574 	ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer);
575 	sshbuf_free(buffer);
576 	pthread_exit(NULL);
577 
578  auth_fail:
579 	if ((r = sshbuf_put_cstring(buffer,
580 	    pam_strerror(sshpam_handle, sshpam_err))) != 0)
581 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
582 	/* XXX - can't do much about an error here */
583 	if (sshpam_err == PAM_ACCT_EXPIRED)
584 		ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer);
585 	else if (sshpam_maxtries_reached)
586 		ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer);
587 	else
588 		ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, buffer);
589 	sshbuf_free(buffer);
590 	pthread_exit(NULL);
591 
592 	return (NULL); /* Avoid warning for non-pthread case */
593 }
594 
595 void
sshpam_thread_cleanup(void)596 sshpam_thread_cleanup(void)
597 {
598 	struct pam_ctxt *ctxt = cleanup_ctxt;
599 
600 	debug3("PAM: %s entering", __func__);
601 	if (ctxt != NULL && ctxt->pam_thread != 0) {
602 		pthread_cancel(ctxt->pam_thread);
603 		pthread_join(ctxt->pam_thread, NULL);
604 		close(ctxt->pam_psock);
605 		close(ctxt->pam_csock);
606 		memset(ctxt, 0, sizeof(*ctxt));
607 		cleanup_ctxt = NULL;
608 	}
609 }
610 
611 static int
sshpam_null_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)612 sshpam_null_conv(int n, sshpam_const struct pam_message **msg,
613     struct pam_response **resp, void *data)
614 {
615 	debug3("PAM: %s entering, %d messages", __func__, n);
616 	return (PAM_CONV_ERR);
617 }
618 
619 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
620 
621 static int
sshpam_store_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)622 sshpam_store_conv(int n, sshpam_const struct pam_message **msg,
623     struct pam_response **resp, void *data)
624 {
625 	struct pam_response *reply;
626 	int r, i;
627 
628 	debug3("PAM: %s called with %d messages", __func__, n);
629 	*resp = NULL;
630 
631 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
632 		return (PAM_CONV_ERR);
633 
634 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
635 		return (PAM_CONV_ERR);
636 
637 	for (i = 0; i < n; ++i) {
638 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
639 		case PAM_ERROR_MSG:
640 		case PAM_TEXT_INFO:
641 			if ((r = sshbuf_putf(loginmsg, "%s\n",
642 			    PAM_MSG_MEMBER(msg, i, msg))) != 0)
643 				fatal("%s: buffer error: %s",
644 				    __func__, ssh_err(r));
645 			reply[i].resp_retcode = PAM_SUCCESS;
646 			break;
647 		default:
648 			goto fail;
649 		}
650 	}
651 	*resp = reply;
652 	return (PAM_SUCCESS);
653 
654  fail:
655 	for(i = 0; i < n; i++) {
656 		free(reply[i].resp);
657 	}
658 	free(reply);
659 	return (PAM_CONV_ERR);
660 }
661 
662 static struct pam_conv store_conv = { sshpam_store_conv, NULL };
663 
664 void
sshpam_cleanup(void)665 sshpam_cleanup(void)
666 {
667 	if (sshpam_handle == NULL || !mm_is_monitor())
668 		return;
669 	debug("PAM: cleanup");
670 	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
671 	if (sshpam_session_open) {
672 		debug("PAM: closing session");
673 		pam_close_session(sshpam_handle, PAM_SILENT);
674 		sshpam_session_open = 0;
675 	}
676 	if (sshpam_cred_established) {
677 		debug("PAM: deleting credentials");
678 		pam_setcred(sshpam_handle, PAM_DELETE_CRED);
679 		sshpam_cred_established = 0;
680 	}
681 	sshpam_authenticated = 0;
682 	pam_end(sshpam_handle, sshpam_err);
683 	sshpam_handle = NULL;
684 }
685 
686 static int
sshpam_init(struct ssh * ssh,Authctxt * authctxt)687 sshpam_init(struct ssh *ssh, Authctxt *authctxt)
688 {
689 	const char *pam_user, *user = authctxt->user;
690 	const char **ptr_pam_user = &pam_user;
691 	int r;
692 
693 	if (options.pam_service_name == NULL)
694 		fatal_f("internal error: NULL PAM service name");
695 #if defined(PAM_SUN_CODEBASE) && defined(PAM_MAX_RESP_SIZE)
696 	/* Protect buggy PAM implementations from excessively long usernames */
697 	if (strlen(user) >= PAM_MAX_RESP_SIZE)
698 		fatal("Username too long from %s port %d",
699 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
700 #endif
701 	if (sshpam_handle == NULL) {
702 		if (ssh == NULL) {
703 			fatal("%s: called initially with no "
704 			    "packet context", __func__);
705 		}
706 	}
707 	if (sshpam_handle != NULL) {
708 		/* We already have a PAM context; check if the user matches */
709 		sshpam_err = pam_get_item(sshpam_handle,
710 		    PAM_USER, (sshpam_const void **)ptr_pam_user);
711 		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
712 			return (0);
713 		pam_end(sshpam_handle, sshpam_err);
714 		sshpam_handle = NULL;
715 	}
716 	debug("PAM: initializing for \"%s\" with service \"%s\"", user,
717 	    options.pam_service_name);
718 	sshpam_err = pam_start(options.pam_service_name, user,
719 	    &store_conv, &sshpam_handle);
720 	sshpam_authctxt = authctxt;
721 
722 	if (sshpam_err != PAM_SUCCESS) {
723 		pam_end(sshpam_handle, sshpam_err);
724 		sshpam_handle = NULL;
725 		return (-1);
726 	}
727 
728 	if (ssh != NULL && sshpam_rhost == NULL) {
729 		/*
730 		 * We need to cache these as we don't have packet context
731 		 * during the kbdint flow.
732 		 */
733 		sshpam_rhost = xstrdup(auth_get_canonical_hostname(ssh,
734 		    options.use_dns));
735 		sshpam_laddr = get_local_ipaddr(
736 		    ssh_packet_get_connection_in(ssh));
737 	}
738 	if (sshpam_rhost != NULL) {
739 		debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost);
740 		sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST,
741 		    sshpam_rhost);
742 		if (sshpam_err != PAM_SUCCESS) {
743 			pam_end(sshpam_handle, sshpam_err);
744 			sshpam_handle = NULL;
745 			return (-1);
746 		}
747 	}
748 	if (ssh != NULL && sshpam_laddr != NULL) {
749 		char *conninfo;
750 
751 		/* Put SSH_CONNECTION in the PAM environment too */
752 		xasprintf(&conninfo, "SSH_CONNECTION=%.50s %d %.50s %d",
753 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
754 		    sshpam_laddr, ssh_local_port(ssh));
755 		if ((r = pam_putenv(sshpam_handle, conninfo)) != PAM_SUCCESS)
756 			logit("pam_putenv: %s", pam_strerror(sshpam_handle, r));
757 		free(conninfo);
758 	}
759 
760 #ifdef PAM_TTY_KLUDGE
761 	/*
762 	 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
763 	 * sshd doesn't set the tty until too late in the auth process and
764 	 * may not even set one (for tty-less connections)
765 	 */
766 	debug("PAM: setting PAM_TTY to \"ssh\"");
767 	sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
768 	if (sshpam_err != PAM_SUCCESS) {
769 		pam_end(sshpam_handle, sshpam_err);
770 		sshpam_handle = NULL;
771 		return (-1);
772 	}
773 #endif
774 	return (0);
775 }
776 
777 static void
expose_authinfo(const char * caller)778 expose_authinfo(const char *caller)
779 {
780 	char *auth_info;
781 
782 	/*
783 	 * Expose authentication information to PAM.
784 	 * The environment variable is versioned. Please increment the
785 	 * version suffix if the format of session_info changes.
786 	 */
787 	if (sshpam_authctxt->session_info == NULL)
788 		auth_info = xstrdup("");
789 	else if ((auth_info = sshbuf_dup_string(
790 	    sshpam_authctxt->session_info)) == NULL)
791 		fatal("%s: sshbuf_dup_string failed", __func__);
792 
793 	debug2("%s: auth information in SSH_AUTH_INFO_0", caller);
794 	do_pam_putenv("SSH_AUTH_INFO_0", auth_info);
795 	free(auth_info);
796 }
797 
798 static void *
sshpam_init_ctx(Authctxt * authctxt)799 sshpam_init_ctx(Authctxt *authctxt)
800 {
801 	struct pam_ctxt *ctxt;
802 	int result, socks[2];
803 
804 	debug3("PAM: %s entering", __func__);
805 	/*
806 	 * Refuse to start if we don't have PAM enabled or do_pam_account
807 	 * has previously failed.
808 	 */
809 	if (!options.use_pam || sshpam_account_status == 0)
810 		return NULL;
811 
812 	/* Initialize PAM */
813 	if (sshpam_init(NULL, authctxt) == -1) {
814 		error("PAM: initialization failed");
815 		return (NULL);
816 	}
817 
818 	expose_authinfo(__func__);
819 	ctxt = xcalloc(1, sizeof *ctxt);
820 
821 	/* Start the authentication thread */
822 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
823 		error("PAM: failed create sockets: %s", strerror(errno));
824 		free(ctxt);
825 		return (NULL);
826 	}
827 	ctxt->pam_psock = socks[0];
828 	ctxt->pam_csock = socks[1];
829 	result = pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt);
830 	if (result != 0) {
831 		error("PAM: failed to start authentication thread: %s",
832 		    strerror(result));
833 		close(socks[0]);
834 		close(socks[1]);
835 		free(ctxt);
836 		return (NULL);
837 	}
838 	cleanup_ctxt = ctxt;
839 	return (ctxt);
840 }
841 
842 static int
sshpam_query(void * ctx,char ** name,char ** info,u_int * num,char *** prompts,u_int ** echo_on)843 sshpam_query(void *ctx, char **name, char **info,
844     u_int *num, char ***prompts, u_int **echo_on)
845 {
846 	struct sshbuf *buffer;
847 	struct pam_ctxt *ctxt = ctx;
848 	size_t plen;
849 	u_char type;
850 	char *msg;
851 	size_t len, mlen, nmesg = 0;
852 	int r;
853 
854 	debug3("PAM: %s entering", __func__);
855 	if ((buffer = sshbuf_new()) == NULL)
856 		fatal("%s: sshbuf_new failed", __func__);
857 	*name = xstrdup("");
858 	*info = xstrdup("");
859 	*prompts = xmalloc(sizeof(char *));
860 	**prompts = NULL;
861 	plen = 0;
862 	*echo_on = xmalloc(sizeof(u_int));
863 	while (ssh_msg_recv(ctxt->pam_psock, buffer) == 0) {
864 		if (++nmesg > PAM_MAX_NUM_MSG)
865 			fatal_f("too many query messages");
866 		if ((r = sshbuf_get_u8(buffer, &type)) != 0 ||
867 		    (r = sshbuf_get_cstring(buffer, &msg, &mlen)) != 0)
868 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
869 		switch (type) {
870 		case PAM_PROMPT_ECHO_ON:
871 		case PAM_PROMPT_ECHO_OFF:
872 			*num = 1;
873 			len = plen + mlen + 1;
874 			**prompts = xreallocarray(**prompts, 1, len);
875 			strlcpy(**prompts + plen, msg, len - plen);
876 			plen += mlen;
877 			**echo_on = (type == PAM_PROMPT_ECHO_ON);
878 			free(msg);
879 			sshbuf_free(buffer);
880 			return (0);
881 		case PAM_ERROR_MSG:
882 		case PAM_TEXT_INFO:
883 			/* accumulate messages */
884 			len = plen + mlen + 2;
885 			**prompts = xreallocarray(**prompts, 1, len);
886 			strlcpy(**prompts + plen, msg, len - plen);
887 			plen += mlen;
888 			strlcat(**prompts + plen, "\n", len - plen);
889 			plen++;
890 			free(msg);
891 			break;
892 		case PAM_ACCT_EXPIRED:
893 		case PAM_MAXTRIES:
894 			if (type == PAM_ACCT_EXPIRED)
895 				sshpam_account_status = 0;
896 			if (type == PAM_MAXTRIES)
897 				sshpam_set_maxtries_reached(1);
898 			/* FALLTHROUGH */
899 		case PAM_AUTH_ERR:
900 			debug3("PAM: %s", pam_strerror(sshpam_handle, type));
901 			if (**prompts != NULL && strlen(**prompts) != 0) {
902 				free(*info);
903 				*info = **prompts;
904 				**prompts = NULL;
905 				*num = 0;
906 				**echo_on = 0;
907 				ctxt->pam_done = -1;
908 				free(msg);
909 				sshbuf_free(buffer);
910 				return 0;
911 			}
912 			/* FALLTHROUGH */
913 		case PAM_SUCCESS:
914 			if (**prompts != NULL) {
915 				/* drain any accumulated messages */
916 				debug("PAM: %s", **prompts);
917 				if ((r = sshbuf_put(loginmsg, **prompts,
918 				    strlen(**prompts))) != 0)
919 					fatal("%s: buffer error: %s",
920 					    __func__, ssh_err(r));
921 				free(**prompts);
922 				**prompts = NULL;
923 			}
924 			if (type == PAM_SUCCESS) {
925 				if (!sshpam_authctxt->valid ||
926 				    (sshpam_authctxt->pw->pw_uid == 0 &&
927 				    options.permit_root_login != PERMIT_YES))
928 					fatal("Internal error: PAM auth "
929 					    "succeeded when it should have "
930 					    "failed");
931 				import_environments(buffer);
932 				*num = 0;
933 				**echo_on = 0;
934 				ctxt->pam_done = 1;
935 				free(msg);
936 				sshbuf_free(buffer);
937 				return (0);
938 			}
939 			error("PAM: %s for %s%.100s from %.100s", msg,
940 			    sshpam_authctxt->valid ? "" : "illegal user ",
941 			    sshpam_authctxt->user, sshpam_rhost);
942 			/* FALLTHROUGH */
943 		default:
944 			*num = 0;
945 			**echo_on = 0;
946 			free(msg);
947 			ctxt->pam_done = -1;
948 			sshbuf_free(buffer);
949 			return (-1);
950 		}
951 	}
952 	sshbuf_free(buffer);
953 	return (-1);
954 }
955 
956 /*
957  * Returns a junk password of identical length to that the user supplied.
958  * Used to mitigate timing attacks against crypt(3)/PAM stacks that
959  * vary processing time in proportion to password length.
960  */
961 static char *
fake_password(const char * wire_password)962 fake_password(const char *wire_password)
963 {
964 	const char junk[] = "\b\n\r\177INCORRECT";
965 	char *ret = NULL;
966 	size_t i, l = wire_password != NULL ? strlen(wire_password) : 0;
967 
968 	if (l >= INT_MAX)
969 		fatal("%s: password length too long: %zu", __func__, l);
970 
971 	ret = malloc(l + 1);
972 	if (ret == NULL)
973 		return NULL;
974 	for (i = 0; i < l; i++)
975 		ret[i] = junk[i % (sizeof(junk) - 1)];
976 	ret[i] = '\0';
977 	return ret;
978 }
979 
980 /* XXX - see also comment in auth-chall.c:verify_response */
981 static int
sshpam_respond(void * ctx,u_int num,char ** resp)982 sshpam_respond(void *ctx, u_int num, char **resp)
983 {
984 	struct sshbuf *buffer;
985 	struct pam_ctxt *ctxt = ctx;
986 	char *fake;
987 	int r;
988 
989 	debug2("PAM: %s entering, %u responses", __func__, num);
990 	switch (ctxt->pam_done) {
991 	case 1:
992 		sshpam_authenticated = 1;
993 		return (0);
994 	case 0:
995 		break;
996 	default:
997 		return (-1);
998 	}
999 	if (num != 1) {
1000 		error("PAM: expected one response, got %u", num);
1001 		return (-1);
1002 	}
1003 	if ((buffer = sshbuf_new()) == NULL)
1004 		fatal("%s: sshbuf_new failed", __func__);
1005 	if (sshpam_authctxt->valid &&
1006 	    (sshpam_authctxt->pw->pw_uid != 0 ||
1007 	    options.permit_root_login == PERMIT_YES)) {
1008 		if ((r = sshbuf_put_cstring(buffer, *resp)) != 0)
1009 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
1010 	} else {
1011 		fake = fake_password(*resp);
1012 		if ((r = sshbuf_put_cstring(buffer, fake)) != 0)
1013 			fatal("%s: buffer error: %s", __func__, ssh_err(r));
1014 		free(fake);
1015 	}
1016 	if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, buffer) == -1) {
1017 		sshbuf_free(buffer);
1018 		return (-1);
1019 	}
1020 	sshbuf_free(buffer);
1021 	return (1);
1022 }
1023 
1024 static void
sshpam_free_ctx(void * ctxtp)1025 sshpam_free_ctx(void *ctxtp)
1026 {
1027 	struct pam_ctxt *ctxt = ctxtp;
1028 
1029 	debug3("PAM: %s entering", __func__);
1030 	sshpam_thread_cleanup();
1031 	free(ctxt);
1032 	/*
1033 	 * We don't call sshpam_cleanup() here because we may need the PAM
1034 	 * handle at a later stage, e.g. when setting up a session.  It's
1035 	 * still on the cleanup list, so pam_end() *will* be called before
1036 	 * the server process terminates.
1037 	 */
1038 }
1039 
1040 KbdintDevice sshpam_device = {
1041 	"pam",
1042 	sshpam_init_ctx,
1043 	sshpam_query,
1044 	sshpam_respond,
1045 	sshpam_free_ctx
1046 };
1047 
1048 KbdintDevice mm_sshpam_device = {
1049 	"pam",
1050 	mm_sshpam_init_ctx,
1051 	mm_sshpam_query,
1052 	mm_sshpam_respond,
1053 	mm_sshpam_free_ctx
1054 };
1055 
1056 /*
1057  * This replaces auth-pam.c
1058  */
1059 void
start_pam(struct ssh * ssh)1060 start_pam(struct ssh *ssh)
1061 {
1062 	Authctxt *authctxt = (Authctxt *)ssh->authctxt;
1063 
1064 	if (!options.use_pam)
1065 		fatal("PAM: initialisation requested when UsePAM=no");
1066 
1067 	if (sshpam_init(ssh, authctxt) == -1)
1068 		fatal("PAM: initialisation failed");
1069 }
1070 
1071 void
finish_pam(void)1072 finish_pam(void)
1073 {
1074 	sshpam_cleanup();
1075 }
1076 
1077 
1078 u_int
do_pam_account(void)1079 do_pam_account(void)
1080 {
1081 	debug("%s: called", __func__);
1082 	if (sshpam_account_status != -1)
1083 		return (sshpam_account_status);
1084 
1085 	expose_authinfo(__func__);
1086 
1087 	sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
1088 	debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err,
1089 	    pam_strerror(sshpam_handle, sshpam_err));
1090 
1091 	if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) {
1092 		sshpam_account_status = 0;
1093 		return (sshpam_account_status);
1094 	}
1095 
1096 	if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
1097 		sshpam_password_change_required(1);
1098 
1099 	sshpam_account_status = 1;
1100 	return (sshpam_account_status);
1101 }
1102 
1103 void
do_pam_setcred(void)1104 do_pam_setcred(void)
1105 {
1106 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1107 	    (const void *)&store_conv);
1108 	if (sshpam_err != PAM_SUCCESS)
1109 		fatal("PAM: failed to set PAM_CONV: %s",
1110 		    pam_strerror(sshpam_handle, sshpam_err));
1111 	debug("PAM: establishing credentials");
1112 	sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
1113 	if (sshpam_err == PAM_SUCCESS) {
1114 		sshpam_cred_established = 1;
1115 		return;
1116 	}
1117 	if (sshpam_authenticated)
1118 		fatal("PAM: pam_setcred(): %s",
1119 		    pam_strerror(sshpam_handle, sshpam_err));
1120 	else
1121 		debug("PAM: pam_setcred(): %s",
1122 		    pam_strerror(sshpam_handle, sshpam_err));
1123 }
1124 
1125 #if 0
1126 static int
1127 sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
1128     struct pam_response **resp, void *data)
1129 {
1130 	char input[PAM_MAX_MSG_SIZE];
1131 	struct pam_response *reply;
1132 	int i;
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 || !isatty(STDIN_FILENO))
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 			reply[i].resp =
1148 			    read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
1149 			    RP_ALLOW_STDIN);
1150 			reply[i].resp_retcode = PAM_SUCCESS;
1151 			break;
1152 		case PAM_PROMPT_ECHO_ON:
1153 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
1154 			if (fgets(input, sizeof input, stdin) == NULL)
1155 				input[0] = '\0';
1156 			if ((reply[i].resp = strdup(input)) == NULL)
1157 				goto fail;
1158 			reply[i].resp_retcode = PAM_SUCCESS;
1159 			break;
1160 		case PAM_ERROR_MSG:
1161 		case PAM_TEXT_INFO:
1162 			fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
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 tty_conv = { sshpam_tty_conv, NULL };
1181 #endif
1182 
1183 /*
1184  * XXX this should be done in the authentication phase, but ssh1 doesn't
1185  * support that
1186  */
1187 void
do_pam_chauthtok(void)1188 do_pam_chauthtok(void)
1189 {
1190 	fatal("Password expired");
1191 #if 0
1192 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1193 	    (const void *)&tty_conv);
1194 	if (sshpam_err != PAM_SUCCESS)
1195 		fatal("PAM: failed to set PAM_CONV: %s",
1196 		    pam_strerror(sshpam_handle, sshpam_err));
1197 	debug("PAM: changing password");
1198 	sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
1199 	if (sshpam_err != PAM_SUCCESS)
1200 		fatal("PAM: pam_chauthtok(): %s",
1201 		    pam_strerror(sshpam_handle, sshpam_err));
1202 #endif
1203 }
1204 
1205 void
do_pam_session(struct ssh * ssh)1206 do_pam_session(struct ssh *ssh)
1207 {
1208 	debug3("PAM: opening session");
1209 
1210 	expose_authinfo(__func__);
1211 
1212 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1213 	    (const void *)&store_conv);
1214 	if (sshpam_err != PAM_SUCCESS)
1215 		fatal("PAM: failed to set PAM_CONV: %s",
1216 		    pam_strerror(sshpam_handle, sshpam_err));
1217 	sshpam_err = pam_open_session(sshpam_handle, 0);
1218 	if (sshpam_err == PAM_SUCCESS)
1219 		sshpam_session_open = 1;
1220 	else {
1221 		sshpam_session_open = 0;
1222 		auth_restrict_session(ssh);
1223 		error("PAM: pam_open_session(): %s",
1224 		    pam_strerror(sshpam_handle, sshpam_err));
1225 	}
1226 
1227 }
1228 
1229 int
is_pam_session_open(void)1230 is_pam_session_open(void)
1231 {
1232 	return sshpam_session_open;
1233 }
1234 
1235 /*
1236  * Set a PAM environment string. We need to do this so that the session
1237  * modules can handle things like Kerberos/GSI credentials that appear
1238  * during the ssh authentication process.
1239  */
1240 int
do_pam_putenv(char * name,char * value)1241 do_pam_putenv(char *name, char *value)
1242 {
1243 	int ret = 1;
1244 	char *compound;
1245 	size_t len;
1246 
1247 	len = strlen(name) + strlen(value) + 2;
1248 	compound = xmalloc(len);
1249 
1250 	snprintf(compound, len, "%s=%s", name, value);
1251 	ret = pam_putenv(sshpam_handle, compound);
1252 	free(compound);
1253 
1254 	return (ret);
1255 }
1256 
1257 char **
fetch_pam_child_environment(void)1258 fetch_pam_child_environment(void)
1259 {
1260 	return sshpam_env;
1261 }
1262 
1263 char **
fetch_pam_environment(void)1264 fetch_pam_environment(void)
1265 {
1266 	return (pam_getenvlist(sshpam_handle));
1267 }
1268 
1269 void
free_pam_environment(char ** env)1270 free_pam_environment(char **env)
1271 {
1272 	char **envp;
1273 
1274 	if (env == NULL)
1275 		return;
1276 
1277 	for (envp = env; *envp; envp++)
1278 		free(*envp);
1279 	free(env);
1280 }
1281 
1282 /*
1283  * "Blind" conversation function for password authentication.  Assumes that
1284  * echo-off prompts are for the password and stores messages for later
1285  * display.
1286  */
1287 static int
sshpam_passwd_conv(int n,sshpam_const struct pam_message ** msg,struct pam_response ** resp,void * data)1288 sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg,
1289     struct pam_response **resp, void *data)
1290 {
1291 	struct pam_response *reply;
1292 	int r, i;
1293 	size_t len;
1294 
1295 	debug3("PAM: %s called with %d messages", __func__, n);
1296 
1297 	*resp = NULL;
1298 
1299 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
1300 		return (PAM_CONV_ERR);
1301 
1302 	if ((reply = calloc(n, sizeof(*reply))) == NULL)
1303 		return (PAM_CONV_ERR);
1304 
1305 	for (i = 0; i < n; ++i) {
1306 		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
1307 		case PAM_PROMPT_ECHO_OFF:
1308 			if (sshpam_password == NULL)
1309 				goto fail;
1310 			if ((reply[i].resp = strdup(sshpam_password)) == NULL)
1311 				goto fail;
1312 			reply[i].resp_retcode = PAM_SUCCESS;
1313 			break;
1314 		case PAM_ERROR_MSG:
1315 		case PAM_TEXT_INFO:
1316 			len = strlen(PAM_MSG_MEMBER(msg, i, msg));
1317 			if (len > 0) {
1318 				if ((r = sshbuf_putf(loginmsg, "%s\n",
1319 				    PAM_MSG_MEMBER(msg, i, msg))) != 0)
1320 					fatal("%s: buffer error: %s",
1321 					    __func__, ssh_err(r));
1322 			}
1323 			if ((reply[i].resp = strdup("")) == NULL)
1324 				goto fail;
1325 			reply[i].resp_retcode = PAM_SUCCESS;
1326 			break;
1327 		default:
1328 			goto fail;
1329 		}
1330 	}
1331 	*resp = reply;
1332 	return (PAM_SUCCESS);
1333 
1334  fail:
1335 	for(i = 0; i < n; i++) {
1336 		free(reply[i].resp);
1337 	}
1338 	free(reply);
1339 	return (PAM_CONV_ERR);
1340 }
1341 
1342 static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
1343 
1344 /*
1345  * Attempt password authentication via PAM
1346  */
1347 int
sshpam_auth_passwd(Authctxt * authctxt,const char * password)1348 sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1349 {
1350 	int flags = (options.permit_empty_passwd == 0 ?
1351 	    PAM_DISALLOW_NULL_AUTHTOK : 0);
1352 	char *fake = NULL;
1353 
1354 	if (!options.use_pam || sshpam_handle == NULL)
1355 		fatal("PAM: %s called when PAM disabled or failed to "
1356 		    "initialise.", __func__);
1357 
1358 	sshpam_password = password;
1359 	sshpam_authctxt = authctxt;
1360 
1361 	/*
1362 	 * If the user logging in is invalid, or is root but is not permitted
1363 	 * by PermitRootLogin, use an invalid password to prevent leaking
1364 	 * information via timing (eg if the PAM config has a delay on fail).
1365 	 */
1366 	if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
1367 	    options.permit_root_login != PERMIT_YES))
1368 		sshpam_password = fake = fake_password(password);
1369 
1370 	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1371 	    (const void *)&passwd_conv);
1372 	if (sshpam_err != PAM_SUCCESS)
1373 		fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
1374 		    pam_strerror(sshpam_handle, sshpam_err));
1375 
1376 	expose_authinfo(__func__);
1377 
1378 	sshpam_err = pam_authenticate(sshpam_handle, flags);
1379 	sshpam_password = NULL;
1380 	free(fake);
1381 	if (sshpam_err == PAM_MAXTRIES)
1382 		sshpam_set_maxtries_reached(1);
1383 	if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
1384 		debug("PAM: password authentication accepted for %.100s",
1385 		    authctxt->user);
1386 		return 1;
1387 	} else {
1388 		debug("PAM: password authentication failed for %.100s: %s",
1389 		    authctxt->valid ? authctxt->user : "an illegal user",
1390 		    pam_strerror(sshpam_handle, sshpam_err));
1391 		return 0;
1392 	}
1393 }
1394 
1395 int
sshpam_get_maxtries_reached(void)1396 sshpam_get_maxtries_reached(void)
1397 {
1398 	return sshpam_maxtries_reached;
1399 }
1400 
1401 void
sshpam_set_maxtries_reached(int reached)1402 sshpam_set_maxtries_reached(int reached)
1403 {
1404 	if (reached == 0 || sshpam_maxtries_reached)
1405 		return;
1406 	sshpam_maxtries_reached = 1;
1407 	options.password_authentication = 0;
1408 	options.kbd_interactive_authentication = 0;
1409 }
1410 #endif /* USE_PAM */
1411