~ubuntu-branches/ubuntu/lucid/openssh/lucid

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