session.c 66.2 KB
Newer Older
1
/* $OpenBSD: session.c,v 1.307 2018/10/04 00:10:11 djm Exp $ */
2 3 4
/*
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 *                    All rights reserved
5 6 7 8 9 10 11
 *
 * As far as I am concerned, the code I have written for this software
 * can be used freely for any purpose.  Any derived versions of this
 * software must be clearly marked as such, and if the derived work is
 * incompatible with the protocol description in the RFC file, it must be
 * called by a name other than "ssh" or "Secure Shell".
 *
Damien Miller's avatar
Damien Miller committed
12
 * SSH2 support by Markus Friedl.
13
 * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Damien Miller's avatar
Damien Miller committed
34
 */
35 36

#include "includes.h"
37 38

#include <sys/types.h>
39
#include <sys/param.h>
40 41 42
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
43
#include <sys/socket.h>
44
#include <sys/un.h>
45
#include <sys/wait.h>
46

47 48
#include <arpa/inet.h>

djm@openbsd.org's avatar
djm@openbsd.org committed
49
#include <ctype.h>
50
#include <errno.h>
51
#include <fcntl.h>
52
#include <grp.h>
53
#include <netdb.h>
54
#ifdef HAVE_PATHS_H
55
#include <paths.h>
56
#endif
57
#include <pwd.h>
58
#include <signal.h>
59
#include <stdarg.h>
60
#include <stdio.h>
61
#include <stdlib.h>
62
#include <string.h>
63
#include <unistd.h>
deraadt@openbsd.org's avatar
deraadt@openbsd.org committed
64
#include <limits.h>
65

66
#include "openbsd-compat/sys-queue.h"
67
#include "xmalloc.h"
68
#include "ssh.h"
69
#include "ssh2.h"
70
#include "sshpty.h"
71
#include "packet.h"
72 73
#include "sshbuf.h"
#include "ssherr.h"
74
#include "match.h"
75 76
#include "uidswap.h"
#include "compat.h"
77
#include "channels.h"
78
#include "sshkey.h"
79 80 81 82 83
#include "cipher.h"
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
#include "hostfile.h"
Damien Miller's avatar
Damien Miller committed
84
#include "auth.h"
85
#include "auth-options.h"
86
#include "authfd.h"
87 88
#include "pathnames.h"
#include "log.h"
89
#include "misc.h"
90
#include "servconf.h"
91
#include "sshlogin.h"
92 93
#include "serverloop.h"
#include "canohost.h"
94
#include "session.h"
95
#include "kex.h"
96
#include "monitor_wrap.h"
97
#include "sftp.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
98
#include "atomicio.h"
Damien Miller's avatar
Damien Miller committed
99

100
#if defined(KRB5) && defined(USE_AFS)
101 102 103
#include <kafs.h>
#endif

104 105 106 107
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#endif

108 109 110 111 112 113
#define IS_INTERNAL_SFTP(c) \
	(!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \
	 (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \
	  c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \
	  c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t'))

114 115 116
/* func */

Session *session_new(void);
djm@openbsd.org's avatar
djm@openbsd.org committed
117
void	session_set_fds(struct ssh *, Session *, int, int, int, int, int);
118
void	session_pty_cleanup(Session *);
119
void	session_proctitle(Session *);
djm@openbsd.org's avatar
djm@openbsd.org committed
120 121 122 123 124 125
int	session_setup_x11fwd(struct ssh *, Session *);
int	do_exec_pty(struct ssh *, Session *, const char *);
int	do_exec_no_pty(struct ssh *, Session *, const char *);
int	do_exec(struct ssh *, Session *, const char *);
void	do_login(struct ssh *, Session *, const char *);
void	do_child(struct ssh *, Session *, const char *);
126 127 128
#ifdef LOGIN_NEEDS_UTMPX
static void	do_pre_login(Session *s);
#endif
129
void	do_motd(void);
130
int	check_quietlogin(Session *, const char *);
131

djm@openbsd.org's avatar
djm@openbsd.org committed
132
static void do_authenticated2(struct ssh *, Authctxt *);
133

djm@openbsd.org's avatar
djm@openbsd.org committed
134
static int session_pty_req(struct ssh *, Session *);
135

136 137 138 139
/* import */
extern ServerOptions options;
extern char *__progname;
extern int debug_flag;
140
extern u_int utmp_len;
141
extern int startup_pipe;
142
extern void destroy_sensitive_data(void);
143
extern struct sshbuf *loginmsg;
144
extern struct sshauthopt *auth_opts;
djm@openbsd.org's avatar
djm@openbsd.org committed
145
char *tun_fwd_ifnames; /* serverloop.c */
146

Damien Miller's avatar
Damien Miller committed
147
/* original command from peer. */
148
const char *original_command = NULL;
Damien Miller's avatar
Damien Miller committed
149

150
/* data */
151 152 153
static int sessions_first_unused = -1;
static int sessions_nalloc = 0;
static Session *sessions = NULL;
154

155 156 157 158
#define SUBSYSTEM_NONE			0
#define SUBSYSTEM_EXT			1
#define SUBSYSTEM_INT_SFTP		2
#define SUBSYSTEM_INT_SFTP_ERROR	3
159

160
#ifdef HAVE_LOGIN_CAP
161
login_cap_t *lc;
162 163
#endif

164
static int is_child = 0;
djm@openbsd.org's avatar
djm@openbsd.org committed
165
static int in_chroot = 0;
166

djm@openbsd.org's avatar
djm@openbsd.org committed
167 168 169
/* File containing userauth info, if ExposeAuthInfo set */
static char *auth_info_file = NULL;

170 171 172 173 174 175 176
/* Name and directory of socket for authentication agent forwarding. */
static char *auth_sock_name = NULL;
static char *auth_sock_dir = NULL;

/* removes the agent forwarding socket */

static void
177
auth_sock_cleanup_proc(struct passwd *pw)
178 179 180 181 182 183 184 185 186 187 188
{
	if (auth_sock_name != NULL) {
		temporarily_use_uid(pw);
		unlink(auth_sock_name);
		rmdir(auth_sock_dir);
		auth_sock_name = NULL;
		restore_uid();
	}
}

static int
djm@openbsd.org's avatar
djm@openbsd.org committed
189
auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw)
190 191
{
	Channel *nc;
192
	int sock = -1;
193 194 195 196 197 198 199 200 201 202

	if (auth_sock_name != NULL) {
		error("authentication forwarding requested twice.");
		return 0;
	}

	/* Temporarily drop privileged uid for mkdir/bind. */
	temporarily_use_uid(pw);

	/* Allocate a buffer for the socket name, and format the name. */
203
	auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
204 205 206 207 208 209

	/* Create private directory for socket */
	if (mkdtemp(auth_sock_dir) == NULL) {
		packet_send_debug("Agent forwarding disabled: "
		    "mkdtemp() failed: %.100s", strerror(errno));
		restore_uid();
210
		free(auth_sock_dir);
211
		auth_sock_dir = NULL;
212
		goto authsock_err;
213
	}
214 215 216

	xasprintf(&auth_sock_name, "%s/agent.%ld",
	    auth_sock_dir, (long) getpid());
217

218 219
	/* Start a Unix listener on auth_sock_name. */
	sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0);
220 221 222 223

	/* Restore the privileged uid. */
	restore_uid();

224 225
	/* Check for socket/bind/listen failure. */
	if (sock < 0)
226
		goto authsock_err;
227 228

	/* Allocate a channel for the authentication agent socket. */
djm@openbsd.org's avatar
djm@openbsd.org committed
229
	nc = channel_new(ssh, "auth socket",
230 231
	    SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
	    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
232
	    0, "auth socket", 1);
233
	nc->path = xstrdup(auth_sock_name);
234
	return 1;
235 236

 authsock_err:
237
	free(auth_sock_name);
238 239
	if (auth_sock_dir != NULL) {
		rmdir(auth_sock_dir);
240
		free(auth_sock_dir);
241 242 243 244 245 246
	}
	if (sock != -1)
		close(sock);
	auth_sock_name = NULL;
	auth_sock_dir = NULL;
	return 0;
247 248
}

249 250 251
static void
display_loginmsg(void)
{
252 253 254 255 256 257 258 259
	int r;

	if (sshbuf_len(loginmsg) == 0)
		return;
	if ((r = sshbuf_put_u8(loginmsg, 0)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
	printf("%s", (char *)sshbuf_ptr(loginmsg));
	sshbuf_reset(loginmsg);
260
}
261

djm@openbsd.org's avatar
djm@openbsd.org committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
static void
prepare_auth_info_file(struct passwd *pw, struct sshbuf *info)
{
	int fd = -1, success = 0;

	if (!options.expose_userauth_info || info == NULL)
		return;

	temporarily_use_uid(pw);
	auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX");
	if ((fd = mkstemp(auth_info_file)) == -1) {
		error("%s: mkstemp: %s", __func__, strerror(errno));
		goto out;
	}
	if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info),
	    sshbuf_len(info)) != sshbuf_len(info)) {
		error("%s: write: %s", __func__, strerror(errno));
		goto out;
	}
	if (close(fd) != 0) {
		error("%s: close: %s", __func__, strerror(errno));
		goto out;
	}
	success = 1;
 out:
	if (!success) {
		if (fd != -1)
			close(fd);
		free(auth_info_file);
		auth_info_file = NULL;
	}
	restore_uid();
}

296
static void
297
set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
298 299 300 301 302
{
	char *tmp, *cp, *host;
	int port;
	size_t i;

303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) {
		channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL);
		for (i = 0; i < auth_opts->npermitopen; i++) {
			tmp = cp = xstrdup(auth_opts->permitopen[i]);
			/* This shouldn't fail as it has already been checked */
			if ((host = hpdelim(&cp)) == NULL)
				fatal("%s: internal error: hpdelim", __func__);
			host = cleanhostname(host);
			if (cp == NULL || (port = permitopen_port(cp)) < 0)
				fatal("%s: internal error: permitopen port",
				    __func__);
			channel_add_permission(ssh,
			    FORWARD_USER, FORWARD_LOCAL, host, port);
			free(tmp);
		}
	}
	if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) {
		channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE);
		for (i = 0; i < auth_opts->npermitlisten; i++) {
			tmp = cp = xstrdup(auth_opts->permitlisten[i]);
			/* This shouldn't fail as it has already been checked */
			if ((host = hpdelim(&cp)) == NULL)
				fatal("%s: internal error: hpdelim", __func__);
			host = cleanhostname(host);
			if (cp == NULL || (port = permitopen_port(cp)) < 0)
				fatal("%s: internal error: permitlisten port",
				    __func__);
			channel_add_permission(ssh,
			    FORWARD_USER, FORWARD_REMOTE, host, port);
			free(tmp);
		}
334 335 336
	}
}

337
void
djm@openbsd.org's avatar
djm@openbsd.org committed
338
do_authenticated(struct ssh *ssh, Authctxt *authctxt)
339
{
340 341
	setproctitle("%s", authctxt->pw->pw_name);

342 343
	auth_log_authopts("active", auth_opts, 0);

344
	/* setup the channel layer */
345
	/* XXX - streamlocal? */
346
	set_fwdpermit_from_authopts(ssh, auth_opts);
347

348 349 350 351 352 353 354 355 356 357 358 359 360 361
	if (!auth_opts->permit_port_forwarding_flag ||
	    options.disable_forwarding) {
		channel_disable_admin(ssh, FORWARD_LOCAL);
		channel_disable_admin(ssh, FORWARD_REMOTE);
	} else {
		if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
			channel_disable_admin(ssh, FORWARD_LOCAL);
		else
			channel_permit_all(ssh, FORWARD_LOCAL);
		if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0)
			channel_disable_admin(ssh, FORWARD_REMOTE);
		else
			channel_permit_all(ssh, FORWARD_REMOTE);
	}
362 363
	auth_debug_send();

djm@openbsd.org's avatar
djm@openbsd.org committed
364 365
	prepare_auth_info_file(authctxt->pw, authctxt->session_info);

djm@openbsd.org's avatar
djm@openbsd.org committed
366
	do_authenticated2(ssh, authctxt);
djm@openbsd.org's avatar
djm@openbsd.org committed
367

djm@openbsd.org's avatar
djm@openbsd.org committed
368
	do_cleanup(ssh, authctxt);
369 370
}

djm@openbsd.org's avatar
djm@openbsd.org committed
371 372 373 374 375 376 377 378 379 380
/* Check untrusted xauth strings for metacharacters */
static int
xauth_valid_string(const char *s)
{
	size_t i;

	for (i = 0; s[i] != '\0'; i++) {
		if (!isalnum((u_char)s[i]) &&
		    s[i] != '.' && s[i] != ':' && s[i] != '/' &&
		    s[i] != '-' && s[i] != '_')
381
			return 0;
djm@openbsd.org's avatar
djm@openbsd.org committed
382 383 384 385
	}
	return 1;
}

386
#define USE_PIPES 1
387 388 389 390 391
/*
 * This is called to fork and execute a command when we have no tty.  This
 * will call do_child from the child, and server_loop from the parent after
 * setting up file descriptors and such.
 */
392
int
djm@openbsd.org's avatar
djm@openbsd.org committed
393
do_exec_no_pty(struct ssh *ssh, Session *s, const char *command)
394
{
395
	pid_t pid;
396 397
#ifdef USE_PIPES
	int pin[2], pout[2], perr[2];
398

399 400 401
	if (s == NULL)
		fatal("do_exec_no_pty: no session");

402
	/* Allocate pipes for communicating with the program. */
403 404 405 406 407 408 409 410 411 412
	if (pipe(pin) < 0) {
		error("%s: pipe in: %.100s", __func__, strerror(errno));
		return -1;
	}
	if (pipe(pout) < 0) {
		error("%s: pipe out: %.100s", __func__, strerror(errno));
		close(pin[0]);
		close(pin[1]);
		return -1;
	}
413 414 415 416 417 418 419 420
	if (pipe(perr) < 0) {
		error("%s: pipe err: %.100s", __func__,
		    strerror(errno));
		close(pin[0]);
		close(pin[1]);
		close(pout[0]);
		close(pout[1]);
		return -1;
421 422
	}
#else
423
	int inout[2], err[2];
424

425 426 427
	if (s == NULL)
		fatal("do_exec_no_pty: no session");

428
	/* Uses socket pairs to communicate with the program. */
429 430 431 432
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) {
		error("%s: socketpair #1: %.100s", __func__, strerror(errno));
		return -1;
	}
433 434 435 436 437 438
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) {
		error("%s: socketpair #2: %.100s", __func__,
		    strerror(errno));
		close(inout[0]);
		close(inout[1]);
		return -1;
439 440 441
	}
#endif

442
	session_proctitle(s);
443 444

	/* Fork the child. */
445 446 447 448 449 450 451 452
	switch ((pid = fork())) {
	case -1:
		error("%s: fork: %.100s", __func__, strerror(errno));
#ifdef USE_PIPES
		close(pin[0]);
		close(pin[1]);
		close(pout[0]);
		close(pout[1]);
453
		close(perr[0]);
454 455 456 457 458
		close(perr[1]);
#else
		close(inout[0]);
		close(inout[1]);
		close(err[0]);
459
		close(err[1]);
460 461 462
#endif
		return -1;
	case 0:
463
		is_child = 1;
464

465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
		/*
		 * Create a new session and process group since the 4.4BSD
		 * setlogin() affects the entire process group.
		 */
		if (setsid() < 0)
			error("setsid failed: %.100s", strerror(errno));

#ifdef USE_PIPES
		/*
		 * Redirect stdin.  We close the parent side of the socket
		 * pair, and make the child side the standard input.
		 */
		close(pin[1]);
		if (dup2(pin[0], 0) < 0)
			perror("dup2 stdin");
		close(pin[0]);

		/* Redirect stdout. */
		close(pout[0]);
		if (dup2(pout[1], 1) < 0)
			perror("dup2 stdout");
		close(pout[1]);

		/* Redirect stderr. */
489
		close(perr[0]);
490 491 492
		if (dup2(perr[1], 2) < 0)
			perror("dup2 stderr");
		close(perr[1]);
493
#else
494 495 496 497 498 499
		/*
		 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
		 * use the same socket, as some programs (particularly rdist)
		 * seem to depend on it.
		 */
		close(inout[1]);
500
		close(err[1]);
501 502
		if (dup2(inout[0], 0) < 0)	/* stdin */
			perror("dup2 stdin");
503
		if (dup2(inout[0], 1) < 0)	/* stdout (same as stdin) */
504
			perror("dup2 stdout");
505
		close(inout[0]);
506 507
		if (dup2(err[0], 2) < 0)	/* stderr */
			perror("dup2 stderr");
508 509 510
		close(err[0]);
#endif

511
		/* Do processing for the child (exec command etc). */
djm@openbsd.org's avatar
djm@openbsd.org committed
512
		do_child(ssh, s, command);
513
		/* NOTREACHED */
514 515
	default:
		break;
516
	}
517

518
#ifdef HAVE_CYGWIN
519
	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
520
#endif
521

522
	s->pid = pid;
523
	/* Set interactive/non-interactive mode. */
524 525
	packet_set_interactive(s->display != NULL,
	    options.ip_qos_interactive, options.ip_qos_bulk);
526 527 528 529 530 531

	/*
	 * Clear loginmsg, since it's the child's responsibility to display
	 * it to the user, otherwise multiple sessions may accumulate
	 * multiple copies of the login messages.
	 */
532
	sshbuf_reset(loginmsg);
533

534 535 536 537 538 539
#ifdef USE_PIPES
	/* We are the parent.  Close the child sides of the pipes. */
	close(pin[0]);
	close(pout[1]);
	close(perr[1]);

djm@openbsd.org's avatar
djm@openbsd.org committed
540
	session_set_fds(ssh, s, pin[1], pout[0], perr[0],
markus@openbsd.org's avatar
markus@openbsd.org committed
541
	    s->is_subsystem, 0);
542
#else
543 544 545 546 547 548 549 550
	/* We are the parent.  Close the child sides of the socket pairs. */
	close(inout[0]);
	close(err[0]);

	/*
	 * Enter the interactive session.  Note: server_loop must be able to
	 * handle the case that fdin and fdout are the same.
	 */
markus@openbsd.org's avatar
markus@openbsd.org committed
551 552
	session_set_fds(s, inout[1], inout[1], err[1],
	    s->is_subsystem, 0);
553 554
#endif
	return 0;
555 556 557 558 559 560 561 562
}

/*
 * This is called to fork and execute a command when we have a tty.  This
 * will call do_child from the child, and server_loop from the parent after
 * setting up file descriptors, controlling tty, updating wtmp, utmp,
 * lastlog, and other such operations.
 */
563
int
djm@openbsd.org's avatar
djm@openbsd.org committed
564
do_exec_pty(struct ssh *ssh, Session *s, const char *command)
565 566 567 568 569 570 571 572 573
{
	int fdout, ptyfd, ttyfd, ptymaster;
	pid_t pid;

	if (s == NULL)
		fatal("do_exec_pty: no session");
	ptyfd = s->ptyfd;
	ttyfd = s->ttyfd;

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
	/*
	 * Create another descriptor of the pty master side for use as the
	 * standard input.  We could use the original descriptor, but this
	 * simplifies code in server_loop.  The descriptor is bidirectional.
	 * Do this before forking (and cleanup in the child) so as to
	 * detect and gracefully fail out-of-fd conditions.
	 */
	if ((fdout = dup(ptyfd)) < 0) {
		error("%s: dup #1: %s", __func__, strerror(errno));
		close(ttyfd);
		close(ptyfd);
		return -1;
	}
	/* we keep a reference to the pty master */
	if ((ptymaster = dup(ptyfd)) < 0) {
		error("%s: dup #2: %s", __func__, strerror(errno));
		close(ttyfd);
		close(ptyfd);
		close(fdout);
		return -1;
	}

596
	/* Fork the child. */
597 598 599 600 601 602 603 604 605
	switch ((pid = fork())) {
	case -1:
		error("%s: fork: %.100s", __func__, strerror(errno));
		close(fdout);
		close(ptymaster);
		close(ttyfd);
		close(ptyfd);
		return -1;
	case 0:
606
		is_child = 1;
607

608 609 610
		close(fdout);
		close(ptymaster);

611 612 613 614 615 616
		/* Close the master side of the pseudo tty. */
		close(ptyfd);

		/* Make the pseudo tty our controlling tty. */
		pty_make_controlling_tty(&ttyfd, s->tty);

617 618 619 620 621 622 623
		/* Redirect stdin/stdout/stderr from the pseudo tty. */
		if (dup2(ttyfd, 0) < 0)
			error("dup2 stdin: %s", strerror(errno));
		if (dup2(ttyfd, 1) < 0)
			error("dup2 stdout: %s", strerror(errno));
		if (dup2(ttyfd, 2) < 0)
			error("dup2 stderr: %s", strerror(errno));
624 625 626 627

		/* Close the extra descriptor for the pseudo tty. */
		close(ttyfd);

628
		/* record login, etc. similar to login(1) */
otto@openbsd.org's avatar
otto@openbsd.org committed
629
#ifndef HAVE_OSF_SIA
djm@openbsd.org's avatar
djm@openbsd.org committed
630
		do_login(ssh, s, command);
631
#endif
632 633 634 635
		/*
		 * Do common processing for the child, such as execing
		 * the command.
		 */
djm@openbsd.org's avatar
djm@openbsd.org committed
636
		do_child(ssh, s, command);
637
		/* NOTREACHED */
638 639
	default:
		break;
640
	}
641

642
#ifdef HAVE_CYGWIN
643
	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
644
#endif
645

646 647 648 649 650 651
	s->pid = pid;

	/* Parent.  Close the slave side of the pseudo tty. */
	close(ttyfd);

	/* Enter interactive session. */
652
	s->ptymaster = ptymaster;
653 654
	packet_set_interactive(1, 
	    options.ip_qos_interactive, options.ip_qos_bulk);
djm@openbsd.org's avatar
djm@openbsd.org committed
655
	session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1);
656
	return 0;
657 658
}

659
#ifdef LOGIN_NEEDS_UTMPX
660
static void
661 662
do_pre_login(Session *s)
{
663
	struct ssh *ssh = active_state;	/* XXX */
664 665 666 667 668 669 670 671 672
	socklen_t fromlen;
	struct sockaddr_storage from;
	pid_t pid = getpid();

	/*
	 * Get IP address of client. If the connection is not a socket, let
	 * the address be 0.0.0.0.
	 */
	memset(&from, 0, sizeof(from));
673
	fromlen = sizeof(from);
674 675
	if (packet_connection_is_on_socket()) {
		if (getpeername(packet_get_connection_in(),
676
		    (struct sockaddr *)&from, &fromlen) < 0) {
677
			debug("getpeername: %.100s", strerror(errno));
678
			cleanup_exit(255);
679 680 681 682
		}
	}

	record_utmp_only(pid, s->tty, s->pw->pw_name,
683
	    session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns),
684
	    (struct sockaddr *)&from, fromlen);
685 686 687
}
#endif

688 689 690 691
/*
 * This is called to fork and execute a command.  If another command is
 * to be forced, execute that instead.
 */
692
int
djm@openbsd.org's avatar
djm@openbsd.org committed
693
do_exec(struct ssh *ssh, Session *s, const char *command)
694
{
695
	int ret;
djm@openbsd.org's avatar
djm@openbsd.org committed
696 697
	const char *forced = NULL, *tty = NULL;
	char session_type[1024];
698

699 700 701
	if (options.adm_forced_command) {
		original_command = command;
		command = options.adm_forced_command;
702
		forced = "(config)";
703
	} else if (auth_opts->force_command != NULL) {
704
		original_command = command;
705
		command = auth_opts->force_command;
706 707
		forced = "(key-option)";
	}
708
	s->forced = 0;
709
	if (forced != NULL) {
710
		s->forced = 1;
711 712 713 714
		if (IS_INTERNAL_SFTP(command)) {
			s->is_subsystem = s->is_subsystem ?
			    SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
		} else if (s->is_subsystem)
715
			s->is_subsystem = SUBSYSTEM_EXT;
716 717 718 719 720 721 722 723 724 725
		snprintf(session_type, sizeof(session_type),
		    "forced-command %s '%.900s'", forced, command);
	} else if (s->is_subsystem) {
		snprintf(session_type, sizeof(session_type),
		    "subsystem '%.900s'", s->subsys);
	} else if (command == NULL) {
		snprintf(session_type, sizeof(session_type), "shell");
	} else {
		/* NB. we don't log unforced commands to preserve privacy */
		snprintf(session_type, sizeof(session_type), "command");
726 727
	}

728 729 730 731 732 733
	if (s->ttyfd != -1) {
		tty = s->tty;
		if (strncmp(tty, "/dev/", 5) == 0)
			tty += 5;
	}

djm@openbsd.org's avatar
djm@openbsd.org committed
734
	verbose("Starting session: %s%s%s for %s from %.200s port %d id %d",
735 736 737 738
	    session_type,
	    tty == NULL ? "" : " on ",
	    tty == NULL ? "" : tty,
	    s->pw->pw_name,
djm@openbsd.org's avatar
djm@openbsd.org committed
739 740
	    ssh_remote_ipaddr(ssh),
	    ssh_remote_port(ssh),
djm@openbsd.org's avatar
djm@openbsd.org committed
741
	    s->self);
742

743
#ifdef SSH_AUDIT_EVENTS
744 745 746 747 748 749 750 751 752 753
	if (command != NULL)
		PRIVSEP(audit_run_command(command));
	else if (s->ttyfd == -1) {
		char *shell = s->pw->pw_shell;

		if (shell[0] == '\0')	/* empty shell means /bin/sh */
			shell =_PATH_BSHELL;
		PRIVSEP(audit_run_command(shell));
	}
#endif
754
	if (s->ttyfd != -1)
djm@openbsd.org's avatar
djm@openbsd.org committed
755
		ret = do_exec_pty(ssh, s, command);
756
	else
djm@openbsd.org's avatar
djm@openbsd.org committed
757
		ret = do_exec_no_pty(ssh, s, command);
758

759
	original_command = NULL;
760

761 762 763 764 765
	/*
	 * Clear loginmsg: it's the child's responsibility to display
	 * it to the user, otherwise multiple sessions may accumulate
	 * multiple copies of the login messages.
	 */
766
	sshbuf_reset(loginmsg);
767 768

	return ret;
769
}
770

771 772
/* administrative, login(1)-like work */
void
djm@openbsd.org's avatar
djm@openbsd.org committed
773
do_login(struct ssh *ssh, Session *s, const char *command)
774 775 776 777 778 779 780 781 782 783 784
{
	socklen_t fromlen;
	struct sockaddr_storage from;
	struct passwd * pw = s->pw;
	pid_t pid = getpid();

	/*
	 * Get IP address of client. If the connection is not a socket, let
	 * the address be 0.0.0.0.
	 */
	memset(&from, 0, sizeof(from));
785
	fromlen = sizeof(from);
786 787
	if (packet_connection_is_on_socket()) {
		if (getpeername(packet_get_connection_in(),
788
		    (struct sockaddr *)&from, &fromlen) < 0) {
789
			debug("getpeername: %.100s", strerror(errno));
790
			cleanup_exit(255);
791 792 793 794
		}
	}

	/* Record that there was a login on that tty from the remote host. */
795 796
	if (!use_privsep)
		record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
djm@openbsd.org's avatar
djm@openbsd.org committed
797
		    session_get_remote_name_or_ip(ssh, utmp_len,
798
		    options.use_dns),
799
		    (struct sockaddr *)&from, fromlen);
800

801 802 803 804 805
#ifdef USE_PAM
	/*
	 * If password change is needed, do it now.
	 * This needs to occur before the ~/.hushlogin check.
	 */
806 807
	if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) {
		display_loginmsg();
808
		do_pam_chauthtok();
809
		s->authctxt->force_pwchange = 0;
810
		/* XXX - signal [net] parent to enable forwardings */
811 812 813
	}
#endif

814
	if (check_quietlogin(s, command))
815 816
		return;

817
	display_loginmsg();
818

819 820 821 822 823 824 825 826 827 828 829 830
	do_motd();
}

/*
 * Display the message of the day.
 */
void
do_motd(void)
{
	FILE *f;
	char buf[256];

831
	if (options.print_motd) {
832 833 834 835
#ifdef HAVE_LOGIN_CAP
		f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
		    "/etc/motd"), "r");
#else
836
		f = fopen("/etc/motd", "r");
837
#endif
838 839 840 841 842 843 844 845
		if (f) {
			while (fgets(buf, sizeof(buf), f))
				fputs(buf, stdout);
			fclose(f);
		}
	}
}

846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870

/*
 * Check for quiet login, either .hushlogin or command given.
 */
int
check_quietlogin(Session *s, const char *command)
{
	char buf[256];
	struct passwd *pw = s->pw;
	struct stat st;

	/* Return 1 if .hushlogin exists or a command given. */
	if (command != NULL)
		return 1;
	snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
#ifdef HAVE_LOGIN_CAP
	if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
		return 1;
#else
	if (stat(buf, &st) >= 0)
		return 1;
#endif
	return 0;
}

871 872 873 874 875
/*
 * Reads environment variables from the given file and adds/overrides them
 * into the environment.  If the file does not exist, this does nothing.
 * Otherwise, it must consist of empty lines, comments (line starts with '#')
 * and assignments of the form name=value.  No other forms are allowed.
876 877
 * If whitelist is not NULL, then it is interpreted as a pattern list and
 * only variable names that match it will be accepted.
878
 */
879
static void
880
read_environment_file(char ***env, u_int *envsize,
881
	const char *filename, const char *whitelist)
882 883
{
	FILE *f;
884 885
	char *line = NULL, *cp, *value;
	size_t linesize = 0;
886
	u_int lineno = 0;
887 888 889 890 891

	f = fopen(filename, "r");
	if (!f)
		return;

892
	while (getline(&line, &linesize, f) != -1) {
893 894
		if (++lineno > 1000)
			fatal("Too many lines in environment file %s", filename);
895
		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
896 897 898
			;
		if (!*cp || *cp == '#' || *cp == '\n')
			continue;
899 900 901

		cp[strcspn(cp, "\n")] = '\0';

902 903
		value = strchr(cp, '=');
		if (value == NULL) {
904 905
			fprintf(stderr, "Bad line %u in %.100s\n", lineno,
			    filename);
906 907
			continue;
		}
908 909 910 911
		/*
		 * Replace the equals sign by nul, and advance value to
		 * the value string.
		 */
912 913
		*value = '\0';
		value++;
914 915 916
		if (whitelist != NULL &&
		    match_pattern_list(cp, whitelist, 0) != 1)
			continue;
917 918
		child_set_env(env, envsize, cp, value);
	}
919
	free(line);
920 921 922
	fclose(f);
}

923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
#ifdef HAVE_ETC_DEFAULT_LOGIN
/*
 * Return named variable from specified environment, or NULL if not present.
 */
static char *
child_get_env(char **env, const char *name)
{
	int i;
	size_t len;

	len = strlen(name);
	for (i=0; env[i] != NULL; i++)
		if (strncmp(name, env[i], len) == 0 && env[i][len] == '=')
			return(env[i] + len + 1);
	return NULL;
}

/*
 * Read /etc/default/login.
 * We pick up the PATH (or SUPATH for root) and UMASK.
 */
static void
read_etc_default_login(char ***env, u_int *envsize, uid_t uid)
{
	char **tmpenv = NULL, *var;
948
	u_int i, tmpenvsize = 0;
949
	u_long mask;
950 951 952 953 954 955

	/*
	 * We don't want to copy the whole file to the child's environment,
	 * so we use a temporary environment and copy the variables we're
	 * interested in.
	 */
956 957
	read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login",
	    options.permit_user_env_whitelist);
958

959 960 961
	if (tmpenv == NULL)
		return;

962 963 964 965 966 967
	if (uid == 0)
		var = child_get_env(tmpenv, "SUPATH");
	else
		var = child_get_env(tmpenv, "PATH");
	if (var != NULL)
		child_set_env(env, envsize, "PATH", var);
968

969 970
	if ((var = child_get_env(tmpenv, "UMASK")) != NULL)
		if (sscanf(var, "%5lo", &mask) == 1)
971
			umask((mode_t)mask);
972

973
	for (i = 0; tmpenv[i] != NULL; i++)
974 975
		free(tmpenv[i]);
	free(tmpenv);
976 977 978
}
#endif /* HAVE_ETC_DEFAULT_LOGIN */

979 980 981
static void
copy_environment_blacklist(char **source, char ***env, u_int *envsize,
    const char *blacklist)
982
{
983
	char *var_name, *var_val;
984 985
	int i;

986
	if (source == NULL)
987
		return;
988

989 990 991
	for(i = 0; source[i] != NULL; i++) {
		var_name = xstrdup(source[i]);
		if ((var_val = strstr(var_name, "=")) == NULL) {
992
			free(var_name);
993 994
			continue;
		}
995
		*var_val++ = '\0';
996

997 998 999 1000 1001
		if (blacklist == NULL ||
		    match_pattern_list(var_name, blacklist, 0) != 1) {
			debug3("Copy environment: %s=%s", var_name, var_val);
			child_set_env(env, envsize, var_name, var_val);
		}
1002

1003
		free(var_name);
1004 1005 1006
	}
}

1007 1008 1009 1010 1011 1012
void
copy_environment(char **source, char ***env, u_int *envsize)
{
	copy_environment_blacklist(source, env, envsize, NULL);
}

1013
static char **
djm@openbsd.org's avatar
djm@openbsd.org committed
1014
do_setup_env(struct ssh *ssh, Session *s, const char *shell)
1015 1016
{
	char buf[256];
1017
	size_t n;
1018
	u_int i, envsize;
1019
	char *ocp, *cp, *value, **env, *laddr;
1020
	struct passwd *pw = s->pw;
1021
#if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
Damien Miller's avatar
Damien Miller committed
1022 1023
	char *path = NULL;
#endif
1024 1025 1026

	/* Initialize the environment. */
	envsize = 100;
1027
	env = xcalloc(envsize, sizeof(char *));
1028 1029
	env[0] = NULL;

1030 1031 1032 1033 1034
#ifdef HAVE_CYGWIN
	/*
	 * The Windows environment contains some setting which are
	 * important for a running system. They must not be dropped.
	 */
1035 1036 1037 1038 1039 1040 1041
	{
		char **p;

		p = fetch_windows_environment();
		copy_environment(p, &env, &envsize);
		free_windows_environment(p);
	}
1042 1043
#endif

1044
#ifdef GSSAPI
1045
	/* Allow any GSSAPI methods that we've used to alter
1046 1047 1048 1049 1050
	 * the childs environment as they see fit
	 */
	ssh_gssapi_do_child(&env, &envsize);
#endif

djm@openbsd.org's avatar
djm@openbsd.org committed
1051 1052 1053
	/* Set basic environment. */
	for (i = 0; i < s->num_env; i++)
		child_set_env(&env, &envsize, s->env[i].name, s->env[i].val);
1054

djm@openbsd.org's avatar
djm@openbsd.org committed
1055 1056
	child_set_env(&env, &envsize, "USER", pw->pw_name);
	child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
1057
#ifdef _AIX
djm@openbsd.org's avatar
djm@openbsd.org committed
1058
	child_set_env(&env, &envsize, "LOGIN", pw->pw_name);
1059
#endif
djm@openbsd.org's avatar
djm@openbsd.org committed
1060
	child_set_env(&env, &envsize, "HOME", pw->pw_dir);
1061
#ifdef HAVE_LOGIN_CAP
djm@openbsd.org's avatar
djm@openbsd.org committed
1062 1063 1064 1065
	if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0)
		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
	else
		child_set_env(&env, &envsize, "PATH", getenv("PATH"));
1066 1067
#else /* HAVE_LOGIN_CAP */
# ifndef HAVE_CYGWIN
djm@openbsd.org's avatar
djm@openbsd.org committed
1068 1069 1070 1071 1072 1073
	/*
	 * There's no standard path on Windows. The path contains
	 * important components pointing to the system directories,
	 * needed for loading shared libraries. So the path better
	 * remains intact here.
	 */
1074
#  ifdef HAVE_ETC_DEFAULT_LOGIN
djm@openbsd.org's avatar
djm@openbsd.org committed
1075 1076
	read_etc_default_login(&env, &envsize, pw->pw_uid);
	path = child_get_env(env, "PATH");
1077
#  endif /* HAVE_ETC_DEFAULT_LOGIN */
djm@openbsd.org's avatar
djm@openbsd.org committed
1078 1079 1080 1081
	if (path == NULL || *path == '\0') {
		child_set_env(&env, &envsize, "PATH",
		    s->pw->pw_uid == 0 ?  SUPERUSER_PATH : _PATH_STDPATH);
	}
1082 1083
# endif /* HAVE_CYGWIN */
#endif /* HAVE_LOGIN_CAP */
1084

djm@openbsd.org's avatar
djm@openbsd.org committed
1085 1086 1087 1088 1089
	snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name);
	child_set_env(&env, &envsize, "MAIL", buf);

	/* Normal systems set SHELL by default. */
	child_set_env(&env, &envsize, "SHELL", shell);
1090 1091 1092

	if (getenv("TZ"))
		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
1093 1094 1095 1096
	if (s->term)
		child_set_env(&env, &envsize, "TERM", s->term);
	if (s->display)
		child_set_env(&env, &envsize, "DISPLAY", s->display);
1097

1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
	/*
	 * Since we clear KRB5CCNAME at startup, if it's set now then it
	 * must have been set by a native authentication method (eg AIX or
	 * SIA), so copy it to the child.
	 */
	{
		char *cp;

		if ((cp = getenv("KRB5CCNAME")) != NULL)
			child_set_env(&env, &envsize, "KRB5CCNAME", cp);
	}

1110
#ifdef _AIX
1111 1112 1113 1114 1115
	{
		char *cp;

		if ((cp = getenv("AUTHSTATE")) != NULL)
			child_set_env(&env, &envsize, "AUTHSTATE", cp);
1116 1117
		read_environment_file(&env, &envsize, "/etc/environment",
		    options.permit_user_env_whitelist);
1118
	}
1119
#endif
1120
#ifdef KRB5
1121
	if (s->authctxt->krb5_ccname)
1122
		child_set_env(&env, &envsize, "KRB5CCNAME",
1123
		    s->authctxt->krb5_ccname);
1124
#endif
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
	if (auth_sock_name != NULL)
		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
		    auth_sock_name);


	/* Set custom environment options from pubkey authentication. */
	if (options.permit_user_env) {
		for (n = 0 ; n < auth_opts->nenv; n++) {
			ocp = xstrdup(auth_opts->env[n]);
			cp = strchr(ocp, '=');
			if (*cp == '=') {
				*cp = '\0';
1137 1138 1139 1140 1141 1142
				/* Apply PermitUserEnvironment whitelist */
				if (options.permit_user_env_whitelist == NULL ||
				    match_pattern_list(ocp,
				    options.permit_user_env_whitelist, 0) == 1)
					child_set_env(&env, &envsize,
					    ocp, cp + 1);
1143 1144 1145 1146 1147 1148 1149 1150 1151
			}
			free(ocp);
		}
	}

	/* read $HOME/.ssh/environment. */
	if (options.permit_user_env) {
		snprintf(buf, sizeof buf, "%.200s/.ssh/environment",
		    pw->pw_dir);
1152 1153
		read_environment_file(&env, &envsize, buf,
		    options.permit_user_env_whitelist);
1154 1155
	}

1156
#ifdef USE_PAM
1157 1158 1159 1160
	/*
	 * Pull in any environment variables that may have
	 * been set by PAM.
	 */
djm@openbsd.org's avatar
djm@openbsd.org committed
1161
	if (options.use_pam) {
1162
		char **p;
1163

1164 1165 1166 1167
		/*
		 * Don't allow SSH_AUTH_INFO variables posted to PAM to leak
		 * back into the environment.
		 */
1168
		p = fetch_pam_child_environment();
1169
		copy_environment_blacklist(p, &env, &envsize, "SSH_AUTH_INFO*");
1170
		free_pam_environment(p);
1171

1172
		p = fetch_pam_environment();
1173
		copy_environment_blacklist(p, &env, &envsize, "SSH_AUTH_INFO*");
1174 1175
		free_pam_environment(p);
	}
1176 1177
#endif /* USE_PAM */

1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
	/* Environment specified by admin */
	for (i = 0; i < options.num_setenv; i++) {
		cp = xstrdup(options.setenv[i]);
		if ((value = strchr(cp, '=')) == NULL) {
			/* shouldn't happen; vars are checked in servconf.c */
			fatal("Invalid config SetEnv: %s", options.setenv[i]);
		}
		*value++ = '\0';
		child_set_env(&env, &envsize, cp, value);
	}

1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
	/* SSH_CLIENT deprecated */
	snprintf(buf, sizeof buf, "%.50s %d %d",
	    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
	    ssh_local_port(ssh));
	child_set_env(&env, &envsize, "SSH_CLIENT", buf);

	laddr = get_local_ipaddr(packet_get_connection_in());
	snprintf(buf, sizeof buf, "%.50s %d %.50s %d",
	    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
	    laddr, ssh_local_port(ssh));
	free(laddr);
	child_set_env(&env, &envsize, "SSH_CONNECTION", buf);

	if (tun_fwd_ifnames != NULL)
		child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames);
	if (auth_info_file != NULL)
		child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
	if (s->ttyfd != -1)
		child_set_env(&env, &envsize, "SSH_TTY", s->tty);
	if (original_command)
		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
		    original_command);
1211 1212 1213 1214 1215 1216 1217

	if (debug_flag) {
		/* dump the environment */
		fprintf(stderr, "Environment:\n");
		for (i = 0; env[i]; i++)
			fprintf(stderr, "  %.200s\n", env[i]);
	}
1218 1219 1220 1221 1222 1223 1224 1225
	return env;
}

/*
 * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found
 * first in this order).
 */
static void
1226
do_rc_files(struct ssh *ssh, Session *s, const char *shell)
1227 1228 1229 1230 1231 1232 1233 1234 1235
{
	FILE *f = NULL;
	char cmd[1024];
	int do_xauth;
	struct stat st;

	do_xauth =
	    s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;

1236
	/* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
1237
	if (!s->is_subsystem && options.adm_forced_command == NULL &&
1238
	    auth_opts->permit_user_rc && options.permit_user_rc &&
1239
	    stat(_PATH_SSH_USER_RC, &st) >= 0) {
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
		snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
		    shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
		if (debug_flag)
			fprintf(stderr, "Running %s\n", cmd);
		f = popen(cmd, "w");
		if (f) {
			if (do_xauth)
				fprintf(f, "%s %s\n", s->auth_proto,
				    s->auth_data);
			pclose(f);
		} else
			fprintf(stderr, "Could not run %s\n",
			    _PATH_SSH_USER_RC);
	} else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
		if (debug_flag)
			fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
			    _PATH_SSH_SYSTEM_RC);
		f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
		if (f) {
			if (do_xauth)
				fprintf(f, "%s %s\n", s->auth_proto,
				    s->auth_data);
			pclose(f);
		} else
			fprintf(stderr, "Could not run %s\n",
			    _PATH_SSH_SYSTEM_RC);
	} else if (do_xauth && options.xauth_location != NULL) {
		/* Add authority data to .Xauthority if appropriate. */
		if (debug_flag) {
			fprintf(stderr,
1270
			    "Running %.500s remove %.100s\n",
1271
			    options.xauth_location, s->auth_display);
1272 1273
			fprintf(stderr,
			    "%.500s add %.100s %.100s %.100s\n",
1274 1275 1276 1277 1278 1279 1280
			    options.xauth_location, s->auth_display,
			    s->auth_proto, s->auth_data);
		}
		snprintf(cmd, sizeof cmd, "%s -q -",
		    options.xauth_location);
		f = popen(cmd, "w");
		if (f) {
1281 1282
			fprintf(f, "remove %s\n",
			    s->auth_display);
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
			fprintf(f, "add %s %s %s\n",
			    s->auth_display, s->auth_proto,
			    s->auth_data);
			pclose(f);
		} else {
			fprintf(stderr, "Could not run %s\n",
			    cmd);
		}
	}
}

static void
do_nologin(struct passwd *pw)
{
	FILE *f = NULL;
1298 1299
	char buf[1024], *nl, *def_nl = _PATH_NOLOGIN;
	struct stat sb;
1300 1301

#ifdef HAVE_LOGIN_CAP
1302
	if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0)
1303 1304
		return;
	nl = login_getcapstr(lc, "nologin", def_nl, def_nl);
1305
#else
1306 1307 1308
	if (pw->pw_uid == 0)
		return;
	nl = def_nl;
1309
#endif
1310 1311
	if (stat(nl, &sb) == -1) {
		if (nl != def_nl)
1312
			free(nl);
1313
		return;
1314
	}
1315 1316 1317 1318

	/* /etc/nologin exists.  Print its contents if we can and exit. */
	logit("User %.100s not allowed because %s exists", pw->pw_name, nl);
	if ((f = fopen(nl, "r")) != NULL) {
1319 1320 1321 1322
		while (fgets(buf, sizeof(buf), f))
			fputs(buf, stderr);
		fclose(f);
	}
1323
	exit(254);
1324 1325
}

1326 1327 1328 1329 1330 1331 1332 1333
/*
 * Chroot into a directory after checking it for safety: all path components
 * must be root-owned directories with strict permissions.
 */
static void
safely_chroot(const char *path, uid_t uid)
{
	const char *cp;
deraadt@openbsd.org's avatar
deraadt@openbsd.org committed
1334
	char component[PATH_MAX];
1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
	struct stat st;

	if (*path != '/')
		fatal("chroot path does not begin at root");
	if (strlen(path) >= sizeof(component))
		fatal("chroot path too long");

	/*
	 * Descend the path, checking that each component is a
	 * root-owned directory with strict permissions.
	 */
	for (cp = path; cp != NULL;) {
		if ((cp = strchr(cp, '/')) == NULL)
			strlcpy(component, path, sizeof(component));
		else {
			cp++;
			memcpy(component, path, cp - path);
			component[cp - path] = '\0';
		}
	
		debug3("%s: checking '%s'", __func__, component);

		if (stat(component, &st) != 0)
			fatal("%s: stat(\"%s\"): %s", __func__,
			    component, strerror(errno));
		if (st.st_uid != 0 || (st.st_mode & 022) != 0)
			fatal("bad ownership or modes for chroot "
			    "directory %s\"%s\"", 
			    cp == NULL ? "" : "component ", component);
		if (!S_ISDIR(st.st_mode))
			fatal("chroot path %s\"%s\" is not a directory",
			    cp == NULL ? "" : "component ", component);

	}

	if (chdir(path) == -1)
		fatal("Unable to chdir to chroot path \"%s\": "
		    "%s", path, strerror(errno));
	if (chroot(path) == -1)
		fatal("chroot(\"%s\"): %s", path, strerror(errno));
	if (chdir("/") == -1)
		fatal("%s: chdir(/) after chroot: %s",
		    __func__, strerror(errno));
	verbose("Changed root directory to \"%s\"", path);
}

1381
/* Set login name, uid, gid, and groups. */
1382
void
1383
do_setusercontext(struct passwd *pw, const char *role)