xref: /dflybsd-src/crypto/openssh/session.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
1*ba1276acSMatthew Dillon /* $OpenBSD: session.c,v 1.338 2024/05/17 00:30:24 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
418de8d7fSPeter Avalos  *                    All rights reserved
518de8d7fSPeter Avalos  *
618de8d7fSPeter Avalos  * As far as I am concerned, the code I have written for this software
718de8d7fSPeter Avalos  * can be used freely for any purpose.  Any derived versions of this
818de8d7fSPeter Avalos  * software must be clearly marked as such, and if the derived work is
918de8d7fSPeter Avalos  * incompatible with the protocol description in the RFC file, it must be
1018de8d7fSPeter Avalos  * called by a name other than "ssh" or "Secure Shell".
1118de8d7fSPeter Avalos  *
1218de8d7fSPeter Avalos  * SSH2 support by Markus Friedl.
1318de8d7fSPeter Avalos  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
1418de8d7fSPeter Avalos  *
1518de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
1618de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
1718de8d7fSPeter Avalos  * are met:
1818de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
1918de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
2018de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
2118de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
2218de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
2318de8d7fSPeter Avalos  *
2418de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2518de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2618de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2718de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2818de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2918de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3018de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3118de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3218de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3318de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3418de8d7fSPeter Avalos  */
3518de8d7fSPeter Avalos 
3618de8d7fSPeter Avalos #include "includes.h"
3718de8d7fSPeter Avalos 
3818de8d7fSPeter Avalos #include <sys/types.h>
3918de8d7fSPeter Avalos #ifdef HAVE_SYS_STAT_H
4018de8d7fSPeter Avalos # include <sys/stat.h>
4118de8d7fSPeter Avalos #endif
4218de8d7fSPeter Avalos #include <sys/socket.h>
4318de8d7fSPeter Avalos #include <sys/un.h>
4418de8d7fSPeter Avalos #include <sys/wait.h>
4518de8d7fSPeter Avalos 
4618de8d7fSPeter Avalos #include <arpa/inet.h>
4718de8d7fSPeter Avalos 
48e9778795SPeter Avalos #include <ctype.h>
4918de8d7fSPeter Avalos #include <errno.h>
50856ea928SPeter Avalos #include <fcntl.h>
5118de8d7fSPeter Avalos #include <grp.h>
5236e94dc5SPeter Avalos #include <netdb.h>
5318de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
5418de8d7fSPeter Avalos #include <paths.h>
5518de8d7fSPeter Avalos #endif
5618de8d7fSPeter Avalos #include <pwd.h>
5718de8d7fSPeter Avalos #include <signal.h>
5818de8d7fSPeter Avalos #include <stdio.h>
5918de8d7fSPeter Avalos #include <stdlib.h>
6018de8d7fSPeter Avalos #include <string.h>
610cbfa66cSDaniel Fojt #include <stdarg.h>
6218de8d7fSPeter Avalos #include <unistd.h>
63e9778795SPeter Avalos #include <limits.h>
6418de8d7fSPeter Avalos 
6518de8d7fSPeter Avalos #include "openbsd-compat/sys-queue.h"
6618de8d7fSPeter Avalos #include "xmalloc.h"
6718de8d7fSPeter Avalos #include "ssh.h"
6818de8d7fSPeter Avalos #include "ssh2.h"
6918de8d7fSPeter Avalos #include "sshpty.h"
7018de8d7fSPeter Avalos #include "packet.h"
71664f4763Szrj #include "sshbuf.h"
72664f4763Szrj #include "ssherr.h"
7318de8d7fSPeter Avalos #include "match.h"
7418de8d7fSPeter Avalos #include "uidswap.h"
7518de8d7fSPeter Avalos #include "channels.h"
76664f4763Szrj #include "sshkey.h"
7718de8d7fSPeter Avalos #include "cipher.h"
7818de8d7fSPeter Avalos #ifdef GSSAPI
7918de8d7fSPeter Avalos #include "ssh-gss.h"
8018de8d7fSPeter Avalos #endif
8118de8d7fSPeter Avalos #include "hostfile.h"
8218de8d7fSPeter Avalos #include "auth.h"
8318de8d7fSPeter Avalos #include "auth-options.h"
8436e94dc5SPeter Avalos #include "authfd.h"
8518de8d7fSPeter Avalos #include "pathnames.h"
8618de8d7fSPeter Avalos #include "log.h"
8736e94dc5SPeter Avalos #include "misc.h"
8818de8d7fSPeter Avalos #include "servconf.h"
8918de8d7fSPeter Avalos #include "sshlogin.h"
9018de8d7fSPeter Avalos #include "serverloop.h"
9118de8d7fSPeter Avalos #include "canohost.h"
9218de8d7fSPeter Avalos #include "session.h"
9318de8d7fSPeter Avalos #include "kex.h"
9418de8d7fSPeter Avalos #include "monitor_wrap.h"
9518de8d7fSPeter Avalos #include "sftp.h"
962c9c1408SMatthew Dillon #include "atomicio.h"
9718de8d7fSPeter Avalos 
9818de8d7fSPeter Avalos #if defined(KRB5) && defined(USE_AFS)
9918de8d7fSPeter Avalos #include <kafs.h>
10018de8d7fSPeter Avalos #endif
10118de8d7fSPeter Avalos 
1021c188a7fSPeter Avalos #ifdef WITH_SELINUX
1031c188a7fSPeter Avalos #include <selinux/selinux.h>
1041c188a7fSPeter Avalos #endif
1051c188a7fSPeter Avalos 
106*ba1276acSMatthew Dillon /*
107*ba1276acSMatthew Dillon  * Hack for systems that do not support FD passing: allocate PTYs directly
108*ba1276acSMatthew Dillon  * without calling into the monitor. This requires either the post-auth
109*ba1276acSMatthew Dillon  * privsep process retain root privileges (see the comment in
110*ba1276acSMatthew Dillon  * sshd-session.c:privsep_postauth) or that PTY allocation doesn't require
111*ba1276acSMatthew Dillon  * privileges to begin with (e.g. Cygwin).
112*ba1276acSMatthew Dillon  */
113*ba1276acSMatthew Dillon #ifdef DISABLE_FD_PASSING
114*ba1276acSMatthew Dillon #define mm_pty_allocate pty_allocate
115*ba1276acSMatthew Dillon #endif
116*ba1276acSMatthew Dillon 
117cb5eb4f1SPeter Avalos #define IS_INTERNAL_SFTP(c) \
118cb5eb4f1SPeter Avalos 	(!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \
119cb5eb4f1SPeter Avalos 	 (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \
120cb5eb4f1SPeter Avalos 	  c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \
121cb5eb4f1SPeter Avalos 	  c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t'))
122cb5eb4f1SPeter Avalos 
12318de8d7fSPeter Avalos /* func */
12418de8d7fSPeter Avalos 
12518de8d7fSPeter Avalos Session *session_new(void);
1262c9c1408SMatthew Dillon void	session_set_fds(struct ssh *, Session *, int, int, int, int, int);
12718de8d7fSPeter Avalos void	session_pty_cleanup(Session *);
12818de8d7fSPeter Avalos void	session_proctitle(Session *);
1292c9c1408SMatthew Dillon int	session_setup_x11fwd(struct ssh *, Session *);
1302c9c1408SMatthew Dillon int	do_exec_pty(struct ssh *, Session *, const char *);
1312c9c1408SMatthew Dillon int	do_exec_no_pty(struct ssh *, Session *, const char *);
1322c9c1408SMatthew Dillon int	do_exec(struct ssh *, Session *, const char *);
1332c9c1408SMatthew Dillon void	do_login(struct ssh *, Session *, const char *);
1342c9c1408SMatthew Dillon void	do_child(struct ssh *, Session *, const char *);
13518de8d7fSPeter Avalos void	do_motd(void);
13618de8d7fSPeter Avalos int	check_quietlogin(Session *, const char *);
13718de8d7fSPeter Avalos 
1382c9c1408SMatthew Dillon static void do_authenticated2(struct ssh *, Authctxt *);
13918de8d7fSPeter Avalos 
1402c9c1408SMatthew Dillon static int session_pty_req(struct ssh *, Session *);
14118de8d7fSPeter Avalos 
14218de8d7fSPeter Avalos /* import */
14318de8d7fSPeter Avalos extern ServerOptions options;
14418de8d7fSPeter Avalos extern char *__progname;
14518de8d7fSPeter Avalos extern int debug_flag;
14618de8d7fSPeter Avalos extern u_int utmp_len;
14718de8d7fSPeter Avalos extern int startup_pipe;
14818de8d7fSPeter Avalos extern void destroy_sensitive_data(void);
149664f4763Szrj extern struct sshbuf *loginmsg;
150664f4763Szrj extern struct sshauthopt *auth_opts;
151664f4763Szrj extern char *tun_fwd_ifnames; /* serverloop.c */
15218de8d7fSPeter Avalos 
15318de8d7fSPeter Avalos /* original command from peer. */
15418de8d7fSPeter Avalos const char *original_command = NULL;
15518de8d7fSPeter Avalos 
15618de8d7fSPeter Avalos /* data */
15718de8d7fSPeter Avalos static int sessions_first_unused = -1;
15818de8d7fSPeter Avalos static int sessions_nalloc = 0;
15918de8d7fSPeter Avalos static Session *sessions = NULL;
16018de8d7fSPeter Avalos 
16118de8d7fSPeter Avalos #define SUBSYSTEM_NONE			0
16218de8d7fSPeter Avalos #define SUBSYSTEM_EXT			1
16318de8d7fSPeter Avalos #define SUBSYSTEM_INT_SFTP		2
164856ea928SPeter Avalos #define SUBSYSTEM_INT_SFTP_ERROR	3
16518de8d7fSPeter Avalos 
16618de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
16718de8d7fSPeter Avalos login_cap_t *lc;
16818de8d7fSPeter Avalos #endif
16918de8d7fSPeter Avalos 
17018de8d7fSPeter Avalos static int is_child = 0;
171e9778795SPeter Avalos static int in_chroot = 0;
17218de8d7fSPeter Avalos 
1732c9c1408SMatthew Dillon /* File containing userauth info, if ExposeAuthInfo set */
1742c9c1408SMatthew Dillon static char *auth_info_file = NULL;
1752c9c1408SMatthew Dillon 
17618de8d7fSPeter Avalos /* Name and directory of socket for authentication agent forwarding. */
17718de8d7fSPeter Avalos static char *auth_sock_name = NULL;
17818de8d7fSPeter Avalos static char *auth_sock_dir = NULL;
17918de8d7fSPeter Avalos 
18018de8d7fSPeter Avalos /* removes the agent forwarding socket */
18118de8d7fSPeter Avalos 
18218de8d7fSPeter Avalos static void
auth_sock_cleanup_proc(struct passwd * pw)18318de8d7fSPeter Avalos auth_sock_cleanup_proc(struct passwd *pw)
18418de8d7fSPeter Avalos {
18518de8d7fSPeter Avalos 	if (auth_sock_name != NULL) {
18618de8d7fSPeter Avalos 		temporarily_use_uid(pw);
18718de8d7fSPeter Avalos 		unlink(auth_sock_name);
18818de8d7fSPeter Avalos 		rmdir(auth_sock_dir);
18918de8d7fSPeter Avalos 		auth_sock_name = NULL;
19018de8d7fSPeter Avalos 		restore_uid();
19118de8d7fSPeter Avalos 	}
19218de8d7fSPeter Avalos }
19318de8d7fSPeter Avalos 
19418de8d7fSPeter Avalos static int
auth_input_request_forwarding(struct ssh * ssh,struct passwd * pw)1952c9c1408SMatthew Dillon auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw)
19618de8d7fSPeter Avalos {
19718de8d7fSPeter Avalos 	Channel *nc;
19818de8d7fSPeter Avalos 	int sock = -1;
19918de8d7fSPeter Avalos 
20018de8d7fSPeter Avalos 	if (auth_sock_name != NULL) {
20118de8d7fSPeter Avalos 		error("authentication forwarding requested twice.");
20218de8d7fSPeter Avalos 		return 0;
20318de8d7fSPeter Avalos 	}
20418de8d7fSPeter Avalos 
20518de8d7fSPeter Avalos 	/* Temporarily drop privileged uid for mkdir/bind. */
20618de8d7fSPeter Avalos 	temporarily_use_uid(pw);
20718de8d7fSPeter Avalos 
20818de8d7fSPeter Avalos 	/* Allocate a buffer for the socket name, and format the name. */
20918de8d7fSPeter Avalos 	auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
21018de8d7fSPeter Avalos 
21118de8d7fSPeter Avalos 	/* Create private directory for socket */
21218de8d7fSPeter Avalos 	if (mkdtemp(auth_sock_dir) == NULL) {
213664f4763Szrj 		ssh_packet_send_debug(ssh, "Agent forwarding disabled: "
21418de8d7fSPeter Avalos 		    "mkdtemp() failed: %.100s", strerror(errno));
21518de8d7fSPeter Avalos 		restore_uid();
21636e94dc5SPeter Avalos 		free(auth_sock_dir);
21718de8d7fSPeter Avalos 		auth_sock_dir = NULL;
21818de8d7fSPeter Avalos 		goto authsock_err;
21918de8d7fSPeter Avalos 	}
22018de8d7fSPeter Avalos 
22118de8d7fSPeter Avalos 	xasprintf(&auth_sock_name, "%s/agent.%ld",
22218de8d7fSPeter Avalos 	    auth_sock_dir, (long) getpid());
22318de8d7fSPeter Avalos 
22436e94dc5SPeter Avalos 	/* Start a Unix listener on auth_sock_name. */
22536e94dc5SPeter Avalos 	sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0);
22618de8d7fSPeter Avalos 
22718de8d7fSPeter Avalos 	/* Restore the privileged uid. */
22818de8d7fSPeter Avalos 	restore_uid();
22918de8d7fSPeter Avalos 
23036e94dc5SPeter Avalos 	/* Check for socket/bind/listen failure. */
23136e94dc5SPeter Avalos 	if (sock < 0)
23218de8d7fSPeter Avalos 		goto authsock_err;
23318de8d7fSPeter Avalos 
23418de8d7fSPeter Avalos 	/* Allocate a channel for the authentication agent socket. */
235*ba1276acSMatthew Dillon 	nc = channel_new(ssh, "auth-listener",
23618de8d7fSPeter Avalos 	    SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
23718de8d7fSPeter Avalos 	    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
23818de8d7fSPeter Avalos 	    0, "auth socket", 1);
239cb5eb4f1SPeter Avalos 	nc->path = xstrdup(auth_sock_name);
24018de8d7fSPeter Avalos 	return 1;
24118de8d7fSPeter Avalos 
24218de8d7fSPeter Avalos  authsock_err:
24336e94dc5SPeter Avalos 	free(auth_sock_name);
24418de8d7fSPeter Avalos 	if (auth_sock_dir != NULL) {
245664f4763Szrj 		temporarily_use_uid(pw);
24618de8d7fSPeter Avalos 		rmdir(auth_sock_dir);
247664f4763Szrj 		restore_uid();
24836e94dc5SPeter Avalos 		free(auth_sock_dir);
24918de8d7fSPeter Avalos 	}
25018de8d7fSPeter Avalos 	if (sock != -1)
25118de8d7fSPeter Avalos 		close(sock);
25218de8d7fSPeter Avalos 	auth_sock_name = NULL;
25318de8d7fSPeter Avalos 	auth_sock_dir = NULL;
25418de8d7fSPeter Avalos 	return 0;
25518de8d7fSPeter Avalos }
25618de8d7fSPeter Avalos 
25718de8d7fSPeter Avalos static void
display_loginmsg(void)25818de8d7fSPeter Avalos display_loginmsg(void)
25918de8d7fSPeter Avalos {
260664f4763Szrj 	int r;
261664f4763Szrj 
262664f4763Szrj 	if (sshbuf_len(loginmsg) == 0)
263664f4763Szrj 		return;
264664f4763Szrj 	if ((r = sshbuf_put_u8(loginmsg, 0)) != 0)
26550a69bb5SSascha Wildner 		fatal_fr(r, "sshbuf_put_u8");
266664f4763Szrj 	printf("%s", (char *)sshbuf_ptr(loginmsg));
267664f4763Szrj 	sshbuf_reset(loginmsg);
26818de8d7fSPeter Avalos }
26918de8d7fSPeter Avalos 
2702c9c1408SMatthew Dillon static void
prepare_auth_info_file(struct passwd * pw,struct sshbuf * info)2712c9c1408SMatthew Dillon prepare_auth_info_file(struct passwd *pw, struct sshbuf *info)
2722c9c1408SMatthew Dillon {
2732c9c1408SMatthew Dillon 	int fd = -1, success = 0;
2742c9c1408SMatthew Dillon 
2752c9c1408SMatthew Dillon 	if (!options.expose_userauth_info || info == NULL)
2762c9c1408SMatthew Dillon 		return;
2772c9c1408SMatthew Dillon 
2782c9c1408SMatthew Dillon 	temporarily_use_uid(pw);
2792c9c1408SMatthew Dillon 	auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX");
2802c9c1408SMatthew Dillon 	if ((fd = mkstemp(auth_info_file)) == -1) {
28150a69bb5SSascha Wildner 		error_f("mkstemp: %s", strerror(errno));
2822c9c1408SMatthew Dillon 		goto out;
2832c9c1408SMatthew Dillon 	}
2842c9c1408SMatthew Dillon 	if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info),
2852c9c1408SMatthew Dillon 	    sshbuf_len(info)) != sshbuf_len(info)) {
28650a69bb5SSascha Wildner 		error_f("write: %s", strerror(errno));
2872c9c1408SMatthew Dillon 		goto out;
2882c9c1408SMatthew Dillon 	}
2892c9c1408SMatthew Dillon 	if (close(fd) != 0) {
29050a69bb5SSascha Wildner 		error_f("close: %s", strerror(errno));
2912c9c1408SMatthew Dillon 		goto out;
2922c9c1408SMatthew Dillon 	}
2932c9c1408SMatthew Dillon 	success = 1;
2942c9c1408SMatthew Dillon  out:
2952c9c1408SMatthew Dillon 	if (!success) {
2962c9c1408SMatthew Dillon 		if (fd != -1)
2972c9c1408SMatthew Dillon 			close(fd);
2982c9c1408SMatthew Dillon 		free(auth_info_file);
2992c9c1408SMatthew Dillon 		auth_info_file = NULL;
3002c9c1408SMatthew Dillon 	}
3012c9c1408SMatthew Dillon 	restore_uid();
3022c9c1408SMatthew Dillon }
3032c9c1408SMatthew Dillon 
304664f4763Szrj static void
set_fwdpermit_from_authopts(struct ssh * ssh,const struct sshauthopt * opts)305664f4763Szrj set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
306664f4763Szrj {
307664f4763Szrj 	char *tmp, *cp, *host;
308664f4763Szrj 	int port;
309664f4763Szrj 	size_t i;
310664f4763Szrj 
311664f4763Szrj 	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) {
312664f4763Szrj 		channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL);
313664f4763Szrj 		for (i = 0; i < auth_opts->npermitopen; i++) {
314664f4763Szrj 			tmp = cp = xstrdup(auth_opts->permitopen[i]);
315664f4763Szrj 			/* This shouldn't fail as it has already been checked */
316ee116499SAntonio Huete Jimenez 			if ((host = hpdelim2(&cp, NULL)) == NULL)
31750a69bb5SSascha Wildner 				fatal_f("internal error: hpdelim");
318664f4763Szrj 			host = cleanhostname(host);
319664f4763Szrj 			if (cp == NULL || (port = permitopen_port(cp)) < 0)
32050a69bb5SSascha Wildner 				fatal_f("internal error: permitopen port");
321664f4763Szrj 			channel_add_permission(ssh,
322664f4763Szrj 			    FORWARD_USER, FORWARD_LOCAL, host, port);
323664f4763Szrj 			free(tmp);
324664f4763Szrj 		}
325664f4763Szrj 	}
326664f4763Szrj 	if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) {
327664f4763Szrj 		channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE);
328664f4763Szrj 		for (i = 0; i < auth_opts->npermitlisten; i++) {
329664f4763Szrj 			tmp = cp = xstrdup(auth_opts->permitlisten[i]);
330664f4763Szrj 			/* This shouldn't fail as it has already been checked */
331664f4763Szrj 			if ((host = hpdelim(&cp)) == NULL)
33250a69bb5SSascha Wildner 				fatal_f("internal error: hpdelim");
333664f4763Szrj 			host = cleanhostname(host);
334664f4763Szrj 			if (cp == NULL || (port = permitopen_port(cp)) < 0)
33550a69bb5SSascha Wildner 				fatal_f("internal error: permitlisten port");
336664f4763Szrj 			channel_add_permission(ssh,
337664f4763Szrj 			    FORWARD_USER, FORWARD_REMOTE, host, port);
338664f4763Szrj 			free(tmp);
339664f4763Szrj 		}
340664f4763Szrj 	}
341664f4763Szrj }
342664f4763Szrj 
34318de8d7fSPeter Avalos void
do_authenticated(struct ssh * ssh,Authctxt * authctxt)3442c9c1408SMatthew Dillon do_authenticated(struct ssh *ssh, Authctxt *authctxt)
34518de8d7fSPeter Avalos {
34618de8d7fSPeter Avalos 	setproctitle("%s", authctxt->pw->pw_name);
34718de8d7fSPeter Avalos 
348664f4763Szrj 	auth_log_authopts("active", auth_opts, 0);
349664f4763Szrj 
35018de8d7fSPeter Avalos 	/* setup the channel layer */
35136e94dc5SPeter Avalos 	/* XXX - streamlocal? */
352664f4763Szrj 	set_fwdpermit_from_authopts(ssh, auth_opts);
35318de8d7fSPeter Avalos 
354664f4763Szrj 	if (!auth_opts->permit_port_forwarding_flag ||
355664f4763Szrj 	    options.disable_forwarding) {
356664f4763Szrj 		channel_disable_admin(ssh, FORWARD_LOCAL);
357664f4763Szrj 		channel_disable_admin(ssh, FORWARD_REMOTE);
358664f4763Szrj 	} else {
359664f4763Szrj 		if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
360664f4763Szrj 			channel_disable_admin(ssh, FORWARD_LOCAL);
361664f4763Szrj 		else
362664f4763Szrj 			channel_permit_all(ssh, FORWARD_LOCAL);
363664f4763Szrj 		if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0)
364664f4763Szrj 			channel_disable_admin(ssh, FORWARD_REMOTE);
365664f4763Szrj 		else
366664f4763Szrj 			channel_permit_all(ssh, FORWARD_REMOTE);
367664f4763Szrj 	}
368664f4763Szrj 	auth_debug_send(ssh);
369856ea928SPeter Avalos 
3702c9c1408SMatthew Dillon 	prepare_auth_info_file(authctxt->pw, authctxt->session_info);
37118de8d7fSPeter Avalos 
3722c9c1408SMatthew Dillon 	do_authenticated2(ssh, authctxt);
3732c9c1408SMatthew Dillon 
3742c9c1408SMatthew Dillon 	do_cleanup(ssh, authctxt);
37518de8d7fSPeter Avalos }
37618de8d7fSPeter Avalos 
377e9778795SPeter Avalos /* Check untrusted xauth strings for metacharacters */
378e9778795SPeter Avalos static int
xauth_valid_string(const char * s)379e9778795SPeter Avalos xauth_valid_string(const char *s)
380e9778795SPeter Avalos {
381e9778795SPeter Avalos 	size_t i;
382e9778795SPeter Avalos 
383e9778795SPeter Avalos 	for (i = 0; s[i] != '\0'; i++) {
384e9778795SPeter Avalos 		if (!isalnum((u_char)s[i]) &&
385e9778795SPeter Avalos 		    s[i] != '.' && s[i] != ':' && s[i] != '/' &&
386e9778795SPeter Avalos 		    s[i] != '-' && s[i] != '_')
387e9778795SPeter Avalos 			return 0;
388e9778795SPeter Avalos 	}
389e9778795SPeter Avalos 	return 1;
390e9778795SPeter Avalos }
391e9778795SPeter Avalos 
39236e94dc5SPeter Avalos #define USE_PIPES 1
39318de8d7fSPeter Avalos /*
39418de8d7fSPeter Avalos  * This is called to fork and execute a command when we have no tty.  This
39518de8d7fSPeter Avalos  * will call do_child from the child, and server_loop from the parent after
39618de8d7fSPeter Avalos  * setting up file descriptors and such.
39718de8d7fSPeter Avalos  */
39818de8d7fSPeter Avalos int
do_exec_no_pty(struct ssh * ssh,Session * s,const char * command)3992c9c1408SMatthew Dillon do_exec_no_pty(struct ssh *ssh, Session *s, const char *command)
40018de8d7fSPeter Avalos {
40118de8d7fSPeter Avalos 	pid_t pid;
40218de8d7fSPeter Avalos #ifdef USE_PIPES
40318de8d7fSPeter Avalos 	int pin[2], pout[2], perr[2];
40418de8d7fSPeter Avalos 
405856ea928SPeter Avalos 	if (s == NULL)
406856ea928SPeter Avalos 		fatal("do_exec_no_pty: no session");
407856ea928SPeter Avalos 
40818de8d7fSPeter Avalos 	/* Allocate pipes for communicating with the program. */
4090cbfa66cSDaniel Fojt 	if (pipe(pin) == -1) {
41050a69bb5SSascha Wildner 		error_f("pipe in: %.100s", strerror(errno));
41118de8d7fSPeter Avalos 		return -1;
41218de8d7fSPeter Avalos 	}
4130cbfa66cSDaniel Fojt 	if (pipe(pout) == -1) {
41450a69bb5SSascha Wildner 		error_f("pipe out: %.100s", strerror(errno));
41518de8d7fSPeter Avalos 		close(pin[0]);
41618de8d7fSPeter Avalos 		close(pin[1]);
41718de8d7fSPeter Avalos 		return -1;
41818de8d7fSPeter Avalos 	}
4190cbfa66cSDaniel Fojt 	if (pipe(perr) == -1) {
42050a69bb5SSascha Wildner 		error_f("pipe err: %.100s", strerror(errno));
42118de8d7fSPeter Avalos 		close(pin[0]);
42218de8d7fSPeter Avalos 		close(pin[1]);
42318de8d7fSPeter Avalos 		close(pout[0]);
42418de8d7fSPeter Avalos 		close(pout[1]);
42518de8d7fSPeter Avalos 		return -1;
42618de8d7fSPeter Avalos 	}
42718de8d7fSPeter Avalos #else
42818de8d7fSPeter Avalos 	int inout[2], err[2];
42918de8d7fSPeter Avalos 
430856ea928SPeter Avalos 	if (s == NULL)
431856ea928SPeter Avalos 		fatal("do_exec_no_pty: no session");
432856ea928SPeter Avalos 
43318de8d7fSPeter Avalos 	/* Uses socket pairs to communicate with the program. */
4340cbfa66cSDaniel Fojt 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) {
43550a69bb5SSascha Wildner 		error_f("socketpair #1: %.100s", strerror(errno));
43618de8d7fSPeter Avalos 		return -1;
43718de8d7fSPeter Avalos 	}
4380cbfa66cSDaniel Fojt 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) {
43950a69bb5SSascha Wildner 		error_f("socketpair #2: %.100s", strerror(errno));
44018de8d7fSPeter Avalos 		close(inout[0]);
44118de8d7fSPeter Avalos 		close(inout[1]);
44218de8d7fSPeter Avalos 		return -1;
44318de8d7fSPeter Avalos 	}
44418de8d7fSPeter Avalos #endif
44518de8d7fSPeter Avalos 
44618de8d7fSPeter Avalos 	session_proctitle(s);
44718de8d7fSPeter Avalos 
44818de8d7fSPeter Avalos 	/* Fork the child. */
44918de8d7fSPeter Avalos 	switch ((pid = fork())) {
45018de8d7fSPeter Avalos 	case -1:
45150a69bb5SSascha Wildner 		error_f("fork: %.100s", strerror(errno));
45218de8d7fSPeter Avalos #ifdef USE_PIPES
45318de8d7fSPeter Avalos 		close(pin[0]);
45418de8d7fSPeter Avalos 		close(pin[1]);
45518de8d7fSPeter Avalos 		close(pout[0]);
45618de8d7fSPeter Avalos 		close(pout[1]);
45718de8d7fSPeter Avalos 		close(perr[0]);
45818de8d7fSPeter Avalos 		close(perr[1]);
45918de8d7fSPeter Avalos #else
46018de8d7fSPeter Avalos 		close(inout[0]);
46118de8d7fSPeter Avalos 		close(inout[1]);
46218de8d7fSPeter Avalos 		close(err[0]);
46318de8d7fSPeter Avalos 		close(err[1]);
46418de8d7fSPeter Avalos #endif
46518de8d7fSPeter Avalos 		return -1;
46618de8d7fSPeter Avalos 	case 0:
46718de8d7fSPeter Avalos 		is_child = 1;
46818de8d7fSPeter Avalos 
46918de8d7fSPeter Avalos 		/*
47018de8d7fSPeter Avalos 		 * Create a new session and process group since the 4.4BSD
47118de8d7fSPeter Avalos 		 * setlogin() affects the entire process group.
47218de8d7fSPeter Avalos 		 */
4730cbfa66cSDaniel Fojt 		if (setsid() == -1)
47418de8d7fSPeter Avalos 			error("setsid failed: %.100s", strerror(errno));
47518de8d7fSPeter Avalos 
47618de8d7fSPeter Avalos #ifdef USE_PIPES
47718de8d7fSPeter Avalos 		/*
47818de8d7fSPeter Avalos 		 * Redirect stdin.  We close the parent side of the socket
47918de8d7fSPeter Avalos 		 * pair, and make the child side the standard input.
48018de8d7fSPeter Avalos 		 */
48118de8d7fSPeter Avalos 		close(pin[1]);
4820cbfa66cSDaniel Fojt 		if (dup2(pin[0], 0) == -1)
48318de8d7fSPeter Avalos 			perror("dup2 stdin");
48418de8d7fSPeter Avalos 		close(pin[0]);
48518de8d7fSPeter Avalos 
48618de8d7fSPeter Avalos 		/* Redirect stdout. */
48718de8d7fSPeter Avalos 		close(pout[0]);
4880cbfa66cSDaniel Fojt 		if (dup2(pout[1], 1) == -1)
48918de8d7fSPeter Avalos 			perror("dup2 stdout");
49018de8d7fSPeter Avalos 		close(pout[1]);
49118de8d7fSPeter Avalos 
49218de8d7fSPeter Avalos 		/* Redirect stderr. */
49318de8d7fSPeter Avalos 		close(perr[0]);
4940cbfa66cSDaniel Fojt 		if (dup2(perr[1], 2) == -1)
49518de8d7fSPeter Avalos 			perror("dup2 stderr");
49618de8d7fSPeter Avalos 		close(perr[1]);
49718de8d7fSPeter Avalos #else
49818de8d7fSPeter Avalos 		/*
49918de8d7fSPeter Avalos 		 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
50018de8d7fSPeter Avalos 		 * use the same socket, as some programs (particularly rdist)
50118de8d7fSPeter Avalos 		 * seem to depend on it.
50218de8d7fSPeter Avalos 		 */
50318de8d7fSPeter Avalos 		close(inout[1]);
50418de8d7fSPeter Avalos 		close(err[1]);
5050cbfa66cSDaniel Fojt 		if (dup2(inout[0], 0) == -1)	/* stdin */
50618de8d7fSPeter Avalos 			perror("dup2 stdin");
5070cbfa66cSDaniel Fojt 		if (dup2(inout[0], 1) == -1)	/* stdout (same as stdin) */
50818de8d7fSPeter Avalos 			perror("dup2 stdout");
50918de8d7fSPeter Avalos 		close(inout[0]);
5100cbfa66cSDaniel Fojt 		if (dup2(err[0], 2) == -1)	/* stderr */
51118de8d7fSPeter Avalos 			perror("dup2 stderr");
51218de8d7fSPeter Avalos 		close(err[0]);
51318de8d7fSPeter Avalos #endif
51418de8d7fSPeter Avalos 
51518de8d7fSPeter Avalos 		/* Do processing for the child (exec command etc). */
5162c9c1408SMatthew Dillon 		do_child(ssh, s, command);
51718de8d7fSPeter Avalos 		/* NOTREACHED */
51818de8d7fSPeter Avalos 	default:
51918de8d7fSPeter Avalos 		break;
52018de8d7fSPeter Avalos 	}
52118de8d7fSPeter Avalos 
52218de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
52318de8d7fSPeter Avalos 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
52418de8d7fSPeter Avalos #endif
52518de8d7fSPeter Avalos 
52618de8d7fSPeter Avalos 	s->pid = pid;
52718de8d7fSPeter Avalos 	/* Set interactive/non-interactive mode. */
528664f4763Szrj 	ssh_packet_set_interactive(ssh, s->display != NULL,
5299f304aafSPeter Avalos 	    options.ip_qos_interactive, options.ip_qos_bulk);
53018de8d7fSPeter Avalos 
53118de8d7fSPeter Avalos 	/*
53218de8d7fSPeter Avalos 	 * Clear loginmsg, since it's the child's responsibility to display
53318de8d7fSPeter Avalos 	 * it to the user, otherwise multiple sessions may accumulate
53418de8d7fSPeter Avalos 	 * multiple copies of the login messages.
53518de8d7fSPeter Avalos 	 */
536664f4763Szrj 	sshbuf_reset(loginmsg);
53718de8d7fSPeter Avalos 
53818de8d7fSPeter Avalos #ifdef USE_PIPES
53918de8d7fSPeter Avalos 	/* We are the parent.  Close the child sides of the pipes. */
54018de8d7fSPeter Avalos 	close(pin[0]);
54118de8d7fSPeter Avalos 	close(pout[1]);
54218de8d7fSPeter Avalos 	close(perr[1]);
54318de8d7fSPeter Avalos 
5442c9c1408SMatthew Dillon 	session_set_fds(ssh, s, pin[1], pout[0], perr[0],
545856ea928SPeter Avalos 	    s->is_subsystem, 0);
54618de8d7fSPeter Avalos #else
54718de8d7fSPeter Avalos 	/* We are the parent.  Close the child sides of the socket pairs. */
54818de8d7fSPeter Avalos 	close(inout[0]);
54918de8d7fSPeter Avalos 	close(err[0]);
55018de8d7fSPeter Avalos 
55118de8d7fSPeter Avalos 	/*
55218de8d7fSPeter Avalos 	 * Enter the interactive session.  Note: server_loop must be able to
55318de8d7fSPeter Avalos 	 * handle the case that fdin and fdout are the same.
55418de8d7fSPeter Avalos 	 */
555664f4763Szrj 	session_set_fds(ssh, s, inout[1], inout[1], err[1],
556856ea928SPeter Avalos 	    s->is_subsystem, 0);
55718de8d7fSPeter Avalos #endif
55818de8d7fSPeter Avalos 	return 0;
55918de8d7fSPeter Avalos }
56018de8d7fSPeter Avalos 
56118de8d7fSPeter Avalos /*
56218de8d7fSPeter Avalos  * This is called to fork and execute a command when we have a tty.  This
56318de8d7fSPeter Avalos  * will call do_child from the child, and server_loop from the parent after
56418de8d7fSPeter Avalos  * setting up file descriptors, controlling tty, updating wtmp, utmp,
56518de8d7fSPeter Avalos  * lastlog, and other such operations.
56618de8d7fSPeter Avalos  */
56718de8d7fSPeter Avalos int
do_exec_pty(struct ssh * ssh,Session * s,const char * command)5682c9c1408SMatthew Dillon do_exec_pty(struct ssh *ssh, Session *s, const char *command)
56918de8d7fSPeter Avalos {
57018de8d7fSPeter Avalos 	int fdout, ptyfd, ttyfd, ptymaster;
57118de8d7fSPeter Avalos 	pid_t pid;
57218de8d7fSPeter Avalos 
57318de8d7fSPeter Avalos 	if (s == NULL)
57418de8d7fSPeter Avalos 		fatal("do_exec_pty: no session");
57518de8d7fSPeter Avalos 	ptyfd = s->ptyfd;
57618de8d7fSPeter Avalos 	ttyfd = s->ttyfd;
57718de8d7fSPeter Avalos 
57818de8d7fSPeter Avalos 	/*
57918de8d7fSPeter Avalos 	 * Create another descriptor of the pty master side for use as the
58018de8d7fSPeter Avalos 	 * standard input.  We could use the original descriptor, but this
58118de8d7fSPeter Avalos 	 * simplifies code in server_loop.  The descriptor is bidirectional.
58218de8d7fSPeter Avalos 	 * Do this before forking (and cleanup in the child) so as to
58318de8d7fSPeter Avalos 	 * detect and gracefully fail out-of-fd conditions.
58418de8d7fSPeter Avalos 	 */
5850cbfa66cSDaniel Fojt 	if ((fdout = dup(ptyfd)) == -1) {
58650a69bb5SSascha Wildner 		error_f("dup #1: %s", strerror(errno));
58718de8d7fSPeter Avalos 		close(ttyfd);
58818de8d7fSPeter Avalos 		close(ptyfd);
58918de8d7fSPeter Avalos 		return -1;
59018de8d7fSPeter Avalos 	}
59118de8d7fSPeter Avalos 	/* we keep a reference to the pty master */
5920cbfa66cSDaniel Fojt 	if ((ptymaster = dup(ptyfd)) == -1) {
59350a69bb5SSascha Wildner 		error_f("dup #2: %s", strerror(errno));
59418de8d7fSPeter Avalos 		close(ttyfd);
59518de8d7fSPeter Avalos 		close(ptyfd);
59618de8d7fSPeter Avalos 		close(fdout);
59718de8d7fSPeter Avalos 		return -1;
59818de8d7fSPeter Avalos 	}
59918de8d7fSPeter Avalos 
60018de8d7fSPeter Avalos 	/* Fork the child. */
60118de8d7fSPeter Avalos 	switch ((pid = fork())) {
60218de8d7fSPeter Avalos 	case -1:
60350a69bb5SSascha Wildner 		error_f("fork: %.100s", strerror(errno));
60418de8d7fSPeter Avalos 		close(fdout);
60518de8d7fSPeter Avalos 		close(ptymaster);
60618de8d7fSPeter Avalos 		close(ttyfd);
60718de8d7fSPeter Avalos 		close(ptyfd);
60818de8d7fSPeter Avalos 		return -1;
60918de8d7fSPeter Avalos 	case 0:
61018de8d7fSPeter Avalos 		is_child = 1;
61118de8d7fSPeter Avalos 
61218de8d7fSPeter Avalos 		close(fdout);
61318de8d7fSPeter Avalos 		close(ptymaster);
61418de8d7fSPeter Avalos 
61518de8d7fSPeter Avalos 		/* Close the master side of the pseudo tty. */
61618de8d7fSPeter Avalos 		close(ptyfd);
61718de8d7fSPeter Avalos 
61818de8d7fSPeter Avalos 		/* Make the pseudo tty our controlling tty. */
61918de8d7fSPeter Avalos 		pty_make_controlling_tty(&ttyfd, s->tty);
62018de8d7fSPeter Avalos 
62118de8d7fSPeter Avalos 		/* Redirect stdin/stdout/stderr from the pseudo tty. */
6220cbfa66cSDaniel Fojt 		if (dup2(ttyfd, 0) == -1)
62318de8d7fSPeter Avalos 			error("dup2 stdin: %s", strerror(errno));
6240cbfa66cSDaniel Fojt 		if (dup2(ttyfd, 1) == -1)
62518de8d7fSPeter Avalos 			error("dup2 stdout: %s", strerror(errno));
6260cbfa66cSDaniel Fojt 		if (dup2(ttyfd, 2) == -1)
62718de8d7fSPeter Avalos 			error("dup2 stderr: %s", strerror(errno));
62818de8d7fSPeter Avalos 
62918de8d7fSPeter Avalos 		/* Close the extra descriptor for the pseudo tty. */
63018de8d7fSPeter Avalos 		close(ttyfd);
63118de8d7fSPeter Avalos 
63218de8d7fSPeter Avalos 		/* record login, etc. similar to login(1) */
6332c9c1408SMatthew Dillon #ifndef HAVE_OSF_SIA
6342c9c1408SMatthew Dillon 		do_login(ssh, s, command);
63518de8d7fSPeter Avalos #endif
63618de8d7fSPeter Avalos 		/*
63718de8d7fSPeter Avalos 		 * Do common processing for the child, such as execing
63818de8d7fSPeter Avalos 		 * the command.
63918de8d7fSPeter Avalos 		 */
6402c9c1408SMatthew Dillon 		do_child(ssh, s, command);
64118de8d7fSPeter Avalos 		/* NOTREACHED */
64218de8d7fSPeter Avalos 	default:
64318de8d7fSPeter Avalos 		break;
64418de8d7fSPeter Avalos 	}
64518de8d7fSPeter Avalos 
64618de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
64718de8d7fSPeter Avalos 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
64818de8d7fSPeter Avalos #endif
64918de8d7fSPeter Avalos 
65018de8d7fSPeter Avalos 	s->pid = pid;
65118de8d7fSPeter Avalos 
65218de8d7fSPeter Avalos 	/* Parent.  Close the slave side of the pseudo tty. */
65318de8d7fSPeter Avalos 	close(ttyfd);
65418de8d7fSPeter Avalos 
65518de8d7fSPeter Avalos 	/* Enter interactive session. */
65618de8d7fSPeter Avalos 	s->ptymaster = ptymaster;
657664f4763Szrj 	ssh_packet_set_interactive(ssh, 1,
6589f304aafSPeter Avalos 	    options.ip_qos_interactive, options.ip_qos_bulk);
6592c9c1408SMatthew Dillon 	session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1);
66018de8d7fSPeter Avalos 	return 0;
66118de8d7fSPeter Avalos }
66218de8d7fSPeter Avalos 
66318de8d7fSPeter Avalos /*
66418de8d7fSPeter Avalos  * This is called to fork and execute a command.  If another command is
66518de8d7fSPeter Avalos  * to be forced, execute that instead.
66618de8d7fSPeter Avalos  */
66718de8d7fSPeter Avalos int
do_exec(struct ssh * ssh,Session * s,const char * command)6682c9c1408SMatthew Dillon do_exec(struct ssh *ssh, Session *s, const char *command)
66918de8d7fSPeter Avalos {
67018de8d7fSPeter Avalos 	int ret;
671e9778795SPeter Avalos 	const char *forced = NULL, *tty = NULL;
672e9778795SPeter Avalos 	char session_type[1024];
67318de8d7fSPeter Avalos 
67418de8d7fSPeter Avalos 	if (options.adm_forced_command) {
67518de8d7fSPeter Avalos 		original_command = command;
67618de8d7fSPeter Avalos 		command = options.adm_forced_command;
67736e94dc5SPeter Avalos 		forced = "(config)";
678664f4763Szrj 	} else if (auth_opts->force_command != NULL) {
67918de8d7fSPeter Avalos 		original_command = command;
680664f4763Szrj 		command = auth_opts->force_command;
68136e94dc5SPeter Avalos 		forced = "(key-option)";
68236e94dc5SPeter Avalos 	}
683664f4763Szrj 	s->forced = 0;
68436e94dc5SPeter Avalos 	if (forced != NULL) {
685664f4763Szrj 		s->forced = 1;
686856ea928SPeter Avalos 		if (IS_INTERNAL_SFTP(command)) {
687856ea928SPeter Avalos 			s->is_subsystem = s->is_subsystem ?
688856ea928SPeter Avalos 			    SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
689856ea928SPeter Avalos 		} else if (s->is_subsystem)
69018de8d7fSPeter Avalos 			s->is_subsystem = SUBSYSTEM_EXT;
69136e94dc5SPeter Avalos 		snprintf(session_type, sizeof(session_type),
69236e94dc5SPeter Avalos 		    "forced-command %s '%.900s'", forced, command);
69336e94dc5SPeter Avalos 	} else if (s->is_subsystem) {
69436e94dc5SPeter Avalos 		snprintf(session_type, sizeof(session_type),
69536e94dc5SPeter Avalos 		    "subsystem '%.900s'", s->subsys);
69636e94dc5SPeter Avalos 	} else if (command == NULL) {
69736e94dc5SPeter Avalos 		snprintf(session_type, sizeof(session_type), "shell");
69836e94dc5SPeter Avalos 	} else {
69936e94dc5SPeter Avalos 		/* NB. we don't log unforced commands to preserve privacy */
70036e94dc5SPeter Avalos 		snprintf(session_type, sizeof(session_type), "command");
70118de8d7fSPeter Avalos 	}
70218de8d7fSPeter Avalos 
70336e94dc5SPeter Avalos 	if (s->ttyfd != -1) {
70436e94dc5SPeter Avalos 		tty = s->tty;
70536e94dc5SPeter Avalos 		if (strncmp(tty, "/dev/", 5) == 0)
70636e94dc5SPeter Avalos 			tty += 5;
70736e94dc5SPeter Avalos 	}
70836e94dc5SPeter Avalos 
709e9778795SPeter Avalos 	verbose("Starting session: %s%s%s for %s from %.200s port %d id %d",
71036e94dc5SPeter Avalos 	    session_type,
71136e94dc5SPeter Avalos 	    tty == NULL ? "" : " on ",
71236e94dc5SPeter Avalos 	    tty == NULL ? "" : tty,
71336e94dc5SPeter Avalos 	    s->pw->pw_name,
714e9778795SPeter Avalos 	    ssh_remote_ipaddr(ssh),
715e9778795SPeter Avalos 	    ssh_remote_port(ssh),
716e9778795SPeter Avalos 	    s->self);
71736e94dc5SPeter Avalos 
71818de8d7fSPeter Avalos #ifdef SSH_AUDIT_EVENTS
71918de8d7fSPeter Avalos 	if (command != NULL)
720*ba1276acSMatthew Dillon 		mm_audit_run_command(command);
72118de8d7fSPeter Avalos 	else if (s->ttyfd == -1) {
72218de8d7fSPeter Avalos 		char *shell = s->pw->pw_shell;
72318de8d7fSPeter Avalos 
72418de8d7fSPeter Avalos 		if (shell[0] == '\0')	/* empty shell means /bin/sh */
72518de8d7fSPeter Avalos 			shell =_PATH_BSHELL;
726*ba1276acSMatthew Dillon 		mm_audit_run_command(shell);
72718de8d7fSPeter Avalos 	}
72818de8d7fSPeter Avalos #endif
72918de8d7fSPeter Avalos 	if (s->ttyfd != -1)
7302c9c1408SMatthew Dillon 		ret = do_exec_pty(ssh, s, command);
73118de8d7fSPeter Avalos 	else
7322c9c1408SMatthew Dillon 		ret = do_exec_no_pty(ssh, s, command);
73318de8d7fSPeter Avalos 
73418de8d7fSPeter Avalos 	original_command = NULL;
73518de8d7fSPeter Avalos 
73618de8d7fSPeter Avalos 	/*
73718de8d7fSPeter Avalos 	 * Clear loginmsg: it's the child's responsibility to display
73818de8d7fSPeter Avalos 	 * it to the user, otherwise multiple sessions may accumulate
73918de8d7fSPeter Avalos 	 * multiple copies of the login messages.
74018de8d7fSPeter Avalos 	 */
741664f4763Szrj 	sshbuf_reset(loginmsg);
74218de8d7fSPeter Avalos 
74318de8d7fSPeter Avalos 	return ret;
74418de8d7fSPeter Avalos }
74518de8d7fSPeter Avalos 
74618de8d7fSPeter Avalos /* administrative, login(1)-like work */
74718de8d7fSPeter Avalos void
do_login(struct ssh * ssh,Session * s,const char * command)7482c9c1408SMatthew Dillon do_login(struct ssh *ssh, Session *s, const char *command)
74918de8d7fSPeter Avalos {
75018de8d7fSPeter Avalos 	socklen_t fromlen;
75118de8d7fSPeter Avalos 	struct sockaddr_storage from;
75218de8d7fSPeter Avalos 
75318de8d7fSPeter Avalos 	/*
75418de8d7fSPeter Avalos 	 * Get IP address of client. If the connection is not a socket, let
75518de8d7fSPeter Avalos 	 * the address be 0.0.0.0.
75618de8d7fSPeter Avalos 	 */
75718de8d7fSPeter Avalos 	memset(&from, 0, sizeof(from));
75818de8d7fSPeter Avalos 	fromlen = sizeof(from);
759664f4763Szrj 	if (ssh_packet_connection_is_on_socket(ssh)) {
760664f4763Szrj 		if (getpeername(ssh_packet_get_connection_in(ssh),
7610cbfa66cSDaniel Fojt 		    (struct sockaddr *)&from, &fromlen) == -1) {
76218de8d7fSPeter Avalos 			debug("getpeername: %.100s", strerror(errno));
76318de8d7fSPeter Avalos 			cleanup_exit(255);
76418de8d7fSPeter Avalos 		}
76518de8d7fSPeter Avalos 	}
76618de8d7fSPeter Avalos 
76718de8d7fSPeter Avalos 	if (check_quietlogin(s, command))
76818de8d7fSPeter Avalos 		return;
76918de8d7fSPeter Avalos 
77018de8d7fSPeter Avalos 	display_loginmsg();
77118de8d7fSPeter Avalos 
77218de8d7fSPeter Avalos 	do_motd();
77318de8d7fSPeter Avalos }
77418de8d7fSPeter Avalos 
77518de8d7fSPeter Avalos /*
77618de8d7fSPeter Avalos  * Display the message of the day.
77718de8d7fSPeter Avalos  */
77818de8d7fSPeter Avalos void
do_motd(void)77918de8d7fSPeter Avalos do_motd(void)
78018de8d7fSPeter Avalos {
78118de8d7fSPeter Avalos 	FILE *f;
78218de8d7fSPeter Avalos 	char buf[256];
78318de8d7fSPeter Avalos 
78418de8d7fSPeter Avalos 	if (options.print_motd) {
78518de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
78618de8d7fSPeter Avalos 		f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
78718de8d7fSPeter Avalos 		    "/etc/motd"), "r");
78818de8d7fSPeter Avalos #else
78918de8d7fSPeter Avalos 		f = fopen("/etc/motd", "r");
79018de8d7fSPeter Avalos #endif
79118de8d7fSPeter Avalos 		if (f) {
79218de8d7fSPeter Avalos 			while (fgets(buf, sizeof(buf), f))
79318de8d7fSPeter Avalos 				fputs(buf, stdout);
79418de8d7fSPeter Avalos 			fclose(f);
79518de8d7fSPeter Avalos 		}
79618de8d7fSPeter Avalos 	}
79718de8d7fSPeter Avalos }
79818de8d7fSPeter Avalos 
79918de8d7fSPeter Avalos 
80018de8d7fSPeter Avalos /*
80118de8d7fSPeter Avalos  * Check for quiet login, either .hushlogin or command given.
80218de8d7fSPeter Avalos  */
80318de8d7fSPeter Avalos int
check_quietlogin(Session * s,const char * command)80418de8d7fSPeter Avalos check_quietlogin(Session *s, const char *command)
80518de8d7fSPeter Avalos {
80618de8d7fSPeter Avalos 	char buf[256];
80718de8d7fSPeter Avalos 	struct passwd *pw = s->pw;
80818de8d7fSPeter Avalos 	struct stat st;
80918de8d7fSPeter Avalos 
81018de8d7fSPeter Avalos 	/* Return 1 if .hushlogin exists or a command given. */
81118de8d7fSPeter Avalos 	if (command != NULL)
81218de8d7fSPeter Avalos 		return 1;
81318de8d7fSPeter Avalos 	snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
81418de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
81518de8d7fSPeter Avalos 	if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
81618de8d7fSPeter Avalos 		return 1;
81718de8d7fSPeter Avalos #else
81818de8d7fSPeter Avalos 	if (stat(buf, &st) >= 0)
81918de8d7fSPeter Avalos 		return 1;
82018de8d7fSPeter Avalos #endif
82118de8d7fSPeter Avalos 	return 0;
82218de8d7fSPeter Avalos }
82318de8d7fSPeter Avalos 
82418de8d7fSPeter Avalos /*
82518de8d7fSPeter Avalos  * Reads environment variables from the given file and adds/overrides them
82618de8d7fSPeter Avalos  * into the environment.  If the file does not exist, this does nothing.
82718de8d7fSPeter Avalos  * Otherwise, it must consist of empty lines, comments (line starts with '#')
82818de8d7fSPeter Avalos  * and assignments of the form name=value.  No other forms are allowed.
82950a69bb5SSascha Wildner  * If allowlist is not NULL, then it is interpreted as a pattern list and
830664f4763Szrj  * only variable names that match it will be accepted.
83118de8d7fSPeter Avalos  */
83218de8d7fSPeter Avalos static void
read_environment_file(char *** env,u_int * envsize,const char * filename,const char * allowlist)83318de8d7fSPeter Avalos read_environment_file(char ***env, u_int *envsize,
83450a69bb5SSascha Wildner 	const char *filename, const char *allowlist)
83518de8d7fSPeter Avalos {
83618de8d7fSPeter Avalos 	FILE *f;
837664f4763Szrj 	char *line = NULL, *cp, *value;
838664f4763Szrj 	size_t linesize = 0;
83918de8d7fSPeter Avalos 	u_int lineno = 0;
84018de8d7fSPeter Avalos 
84118de8d7fSPeter Avalos 	f = fopen(filename, "r");
84218de8d7fSPeter Avalos 	if (!f)
84318de8d7fSPeter Avalos 		return;
84418de8d7fSPeter Avalos 
845664f4763Szrj 	while (getline(&line, &linesize, f) != -1) {
84618de8d7fSPeter Avalos 		if (++lineno > 1000)
84718de8d7fSPeter Avalos 			fatal("Too many lines in environment file %s", filename);
848664f4763Szrj 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
84918de8d7fSPeter Avalos 			;
85018de8d7fSPeter Avalos 		if (!*cp || *cp == '#' || *cp == '\n')
85118de8d7fSPeter Avalos 			continue;
85218de8d7fSPeter Avalos 
85318de8d7fSPeter Avalos 		cp[strcspn(cp, "\n")] = '\0';
85418de8d7fSPeter Avalos 
85518de8d7fSPeter Avalos 		value = strchr(cp, '=');
85618de8d7fSPeter Avalos 		if (value == NULL) {
85718de8d7fSPeter Avalos 			fprintf(stderr, "Bad line %u in %.100s\n", lineno,
85818de8d7fSPeter Avalos 			    filename);
85918de8d7fSPeter Avalos 			continue;
86018de8d7fSPeter Avalos 		}
86118de8d7fSPeter Avalos 		/*
86218de8d7fSPeter Avalos 		 * Replace the equals sign by nul, and advance value to
86318de8d7fSPeter Avalos 		 * the value string.
86418de8d7fSPeter Avalos 		 */
86518de8d7fSPeter Avalos 		*value = '\0';
86618de8d7fSPeter Avalos 		value++;
86750a69bb5SSascha Wildner 		if (allowlist != NULL &&
86850a69bb5SSascha Wildner 		    match_pattern_list(cp, allowlist, 0) != 1)
869664f4763Szrj 			continue;
87018de8d7fSPeter Avalos 		child_set_env(env, envsize, cp, value);
87118de8d7fSPeter Avalos 	}
872664f4763Szrj 	free(line);
87318de8d7fSPeter Avalos 	fclose(f);
87418de8d7fSPeter Avalos }
87518de8d7fSPeter Avalos 
87618de8d7fSPeter Avalos #ifdef HAVE_ETC_DEFAULT_LOGIN
87718de8d7fSPeter Avalos /*
87818de8d7fSPeter Avalos  * Return named variable from specified environment, or NULL if not present.
87918de8d7fSPeter Avalos  */
88018de8d7fSPeter Avalos static char *
child_get_env(char ** env,const char * name)88118de8d7fSPeter Avalos child_get_env(char **env, const char *name)
88218de8d7fSPeter Avalos {
88318de8d7fSPeter Avalos 	int i;
88418de8d7fSPeter Avalos 	size_t len;
88518de8d7fSPeter Avalos 
88618de8d7fSPeter Avalos 	len = strlen(name);
88718de8d7fSPeter Avalos 	for (i=0; env[i] != NULL; i++)
88818de8d7fSPeter Avalos 		if (strncmp(name, env[i], len) == 0 && env[i][len] == '=')
88918de8d7fSPeter Avalos 			return(env[i] + len + 1);
89018de8d7fSPeter Avalos 	return NULL;
89118de8d7fSPeter Avalos }
89218de8d7fSPeter Avalos 
89318de8d7fSPeter Avalos /*
89418de8d7fSPeter Avalos  * Read /etc/default/login.
89518de8d7fSPeter Avalos  * We pick up the PATH (or SUPATH for root) and UMASK.
89618de8d7fSPeter Avalos  */
89718de8d7fSPeter Avalos static void
read_etc_default_login(char *** env,u_int * envsize,uid_t uid)89818de8d7fSPeter Avalos read_etc_default_login(char ***env, u_int *envsize, uid_t uid)
89918de8d7fSPeter Avalos {
90018de8d7fSPeter Avalos 	char **tmpenv = NULL, *var;
90118de8d7fSPeter Avalos 	u_int i, tmpenvsize = 0;
90218de8d7fSPeter Avalos 	u_long mask;
90318de8d7fSPeter Avalos 
90418de8d7fSPeter Avalos 	/*
90518de8d7fSPeter Avalos 	 * We don't want to copy the whole file to the child's environment,
90618de8d7fSPeter Avalos 	 * so we use a temporary environment and copy the variables we're
90718de8d7fSPeter Avalos 	 * interested in.
90818de8d7fSPeter Avalos 	 */
909664f4763Szrj 	read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login",
91050a69bb5SSascha Wildner 	    options.permit_user_env_allowlist);
91118de8d7fSPeter Avalos 
91218de8d7fSPeter Avalos 	if (tmpenv == NULL)
91318de8d7fSPeter Avalos 		return;
91418de8d7fSPeter Avalos 
91518de8d7fSPeter Avalos 	if (uid == 0)
91618de8d7fSPeter Avalos 		var = child_get_env(tmpenv, "SUPATH");
91718de8d7fSPeter Avalos 	else
91818de8d7fSPeter Avalos 		var = child_get_env(tmpenv, "PATH");
91918de8d7fSPeter Avalos 	if (var != NULL)
92018de8d7fSPeter Avalos 		child_set_env(env, envsize, "PATH", var);
92118de8d7fSPeter Avalos 
92218de8d7fSPeter Avalos 	if ((var = child_get_env(tmpenv, "UMASK")) != NULL)
92318de8d7fSPeter Avalos 		if (sscanf(var, "%5lo", &mask) == 1)
92418de8d7fSPeter Avalos 			umask((mode_t)mask);
92518de8d7fSPeter Avalos 
92618de8d7fSPeter Avalos 	for (i = 0; tmpenv[i] != NULL; i++)
92736e94dc5SPeter Avalos 		free(tmpenv[i]);
92836e94dc5SPeter Avalos 	free(tmpenv);
92918de8d7fSPeter Avalos }
93018de8d7fSPeter Avalos #endif /* HAVE_ETC_DEFAULT_LOGIN */
93118de8d7fSPeter Avalos 
9320cbfa66cSDaniel Fojt #if defined(USE_PAM) || defined(HAVE_CYGWIN)
93372be967aSzrj static void
copy_environment_denylist(char ** source,char *** env,u_int * envsize,const char * denylist)93450a69bb5SSascha Wildner copy_environment_denylist(char **source, char ***env, u_int *envsize,
93550a69bb5SSascha Wildner     const char *denylist)
93618de8d7fSPeter Avalos {
93718de8d7fSPeter Avalos 	char *var_name, *var_val;
93818de8d7fSPeter Avalos 	int i;
93918de8d7fSPeter Avalos 
94018de8d7fSPeter Avalos 	if (source == NULL)
94118de8d7fSPeter Avalos 		return;
94218de8d7fSPeter Avalos 
94318de8d7fSPeter Avalos 	for(i = 0; source[i] != NULL; i++) {
94418de8d7fSPeter Avalos 		var_name = xstrdup(source[i]);
94518de8d7fSPeter Avalos 		if ((var_val = strstr(var_name, "=")) == NULL) {
94636e94dc5SPeter Avalos 			free(var_name);
94718de8d7fSPeter Avalos 			continue;
94818de8d7fSPeter Avalos 		}
94918de8d7fSPeter Avalos 		*var_val++ = '\0';
95018de8d7fSPeter Avalos 
95150a69bb5SSascha Wildner 		if (denylist == NULL ||
95250a69bb5SSascha Wildner 		    match_pattern_list(var_name, denylist, 0) != 1) {
95318de8d7fSPeter Avalos 			debug3("Copy environment: %s=%s", var_name, var_val);
95418de8d7fSPeter Avalos 			child_set_env(env, envsize, var_name, var_val);
9552c9c1408SMatthew Dillon 		}
95618de8d7fSPeter Avalos 
95736e94dc5SPeter Avalos 		free(var_name);
95818de8d7fSPeter Avalos 	}
95918de8d7fSPeter Avalos }
9600cbfa66cSDaniel Fojt #endif /* defined(USE_PAM) || defined(HAVE_CYGWIN) */
96118de8d7fSPeter Avalos 
962ac187a81SSascha Wildner #ifdef HAVE_CYGWIN
963ac187a81SSascha Wildner static void
copy_environment(char ** source,char *** env,u_int * envsize)9642c9c1408SMatthew Dillon copy_environment(char **source, char ***env, u_int *envsize)
96518de8d7fSPeter Avalos {
96650a69bb5SSascha Wildner 	copy_environment_denylist(source, env, envsize, NULL);
9672c9c1408SMatthew Dillon }
968ac187a81SSascha Wildner #endif
969ac187a81SSascha Wildner 
9702c9c1408SMatthew Dillon static char **
do_setup_env(struct ssh * ssh,Session * s,const char * shell)9712c9c1408SMatthew Dillon do_setup_env(struct ssh *ssh, Session *s, const char *shell)
9722c9c1408SMatthew Dillon {
97318de8d7fSPeter Avalos 	char buf[256];
974664f4763Szrj 	size_t n;
97518de8d7fSPeter Avalos 	u_int i, envsize;
976664f4763Szrj 	char *ocp, *cp, *value, **env, *laddr;
97718de8d7fSPeter Avalos 	struct passwd *pw = s->pw;
97840c002afSPeter Avalos #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
97918de8d7fSPeter Avalos 	char *path = NULL;
98018de8d7fSPeter Avalos #endif
98118de8d7fSPeter Avalos 
98218de8d7fSPeter Avalos 	/* Initialize the environment. */
98318de8d7fSPeter Avalos 	envsize = 100;
98418de8d7fSPeter Avalos 	env = xcalloc(envsize, sizeof(char *));
98518de8d7fSPeter Avalos 	env[0] = NULL;
98618de8d7fSPeter Avalos 
98718de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
98818de8d7fSPeter Avalos 	/*
98918de8d7fSPeter Avalos 	 * The Windows environment contains some setting which are
99018de8d7fSPeter Avalos 	 * important for a running system. They must not be dropped.
99118de8d7fSPeter Avalos 	 */
99218de8d7fSPeter Avalos 	{
99318de8d7fSPeter Avalos 		char **p;
99418de8d7fSPeter Avalos 
99518de8d7fSPeter Avalos 		p = fetch_windows_environment();
99618de8d7fSPeter Avalos 		copy_environment(p, &env, &envsize);
99718de8d7fSPeter Avalos 		free_windows_environment(p);
99818de8d7fSPeter Avalos 	}
99918de8d7fSPeter Avalos #endif
100018de8d7fSPeter Avalos 
100118de8d7fSPeter Avalos #ifdef GSSAPI
100218de8d7fSPeter Avalos 	/* Allow any GSSAPI methods that we've used to alter
10030cbfa66cSDaniel Fojt 	 * the child's environment as they see fit
100418de8d7fSPeter Avalos 	 */
100518de8d7fSPeter Avalos 	ssh_gssapi_do_child(&env, &envsize);
100618de8d7fSPeter Avalos #endif
100718de8d7fSPeter Avalos 
100818de8d7fSPeter Avalos 	/* Set basic environment. */
100918de8d7fSPeter Avalos 	for (i = 0; i < s->num_env; i++)
10102c9c1408SMatthew Dillon 		child_set_env(&env, &envsize, s->env[i].name, s->env[i].val);
101118de8d7fSPeter Avalos 
101218de8d7fSPeter Avalos 	child_set_env(&env, &envsize, "USER", pw->pw_name);
101318de8d7fSPeter Avalos 	child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
101418de8d7fSPeter Avalos #ifdef _AIX
101518de8d7fSPeter Avalos 	child_set_env(&env, &envsize, "LOGIN", pw->pw_name);
101618de8d7fSPeter Avalos #endif
101718de8d7fSPeter Avalos 	child_set_env(&env, &envsize, "HOME", pw->pw_dir);
101818de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
101918de8d7fSPeter Avalos 	if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0)
102018de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
102118de8d7fSPeter Avalos 	else
102218de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "PATH", getenv("PATH"));
102318de8d7fSPeter Avalos #else /* HAVE_LOGIN_CAP */
102418de8d7fSPeter Avalos # ifndef HAVE_CYGWIN
102518de8d7fSPeter Avalos 	/*
102618de8d7fSPeter Avalos 	 * There's no standard path on Windows. The path contains
102718de8d7fSPeter Avalos 	 * important components pointing to the system directories,
102818de8d7fSPeter Avalos 	 * needed for loading shared libraries. So the path better
102918de8d7fSPeter Avalos 	 * remains intact here.
103018de8d7fSPeter Avalos 	 */
103118de8d7fSPeter Avalos #  ifdef HAVE_ETC_DEFAULT_LOGIN
103218de8d7fSPeter Avalos 	read_etc_default_login(&env, &envsize, pw->pw_uid);
103318de8d7fSPeter Avalos 	path = child_get_env(env, "PATH");
103418de8d7fSPeter Avalos #  endif /* HAVE_ETC_DEFAULT_LOGIN */
103518de8d7fSPeter Avalos 	if (path == NULL || *path == '\0') {
103618de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "PATH",
10372c9c1408SMatthew Dillon 		    s->pw->pw_uid == 0 ?  SUPERUSER_PATH : _PATH_STDPATH);
103818de8d7fSPeter Avalos 	}
103918de8d7fSPeter Avalos # endif /* HAVE_CYGWIN */
104018de8d7fSPeter Avalos #endif /* HAVE_LOGIN_CAP */
104118de8d7fSPeter Avalos 
1042664f4763Szrj 	if (!options.use_pam) {
1043664f4763Szrj 		snprintf(buf, sizeof buf, "%.200s/%.50s",
1044664f4763Szrj 		    _PATH_MAILDIR, pw->pw_name);
104518de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "MAIL", buf);
1046664f4763Szrj 	}
104718de8d7fSPeter Avalos 
104818de8d7fSPeter Avalos 	/* Normal systems set SHELL by default. */
104918de8d7fSPeter Avalos 	child_set_env(&env, &envsize, "SHELL", shell);
10502c9c1408SMatthew Dillon 
105118de8d7fSPeter Avalos 	if (getenv("TZ"))
105218de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
105318de8d7fSPeter Avalos 	if (s->term)
105418de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "TERM", s->term);
105518de8d7fSPeter Avalos 	if (s->display)
105618de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "DISPLAY", s->display);
105718de8d7fSPeter Avalos 
105818de8d7fSPeter Avalos 	/*
105918de8d7fSPeter Avalos 	 * Since we clear KRB5CCNAME at startup, if it's set now then it
106018de8d7fSPeter Avalos 	 * must have been set by a native authentication method (eg AIX or
106118de8d7fSPeter Avalos 	 * SIA), so copy it to the child.
106218de8d7fSPeter Avalos 	 */
106318de8d7fSPeter Avalos 	{
106418de8d7fSPeter Avalos 		char *cp;
106518de8d7fSPeter Avalos 
106618de8d7fSPeter Avalos 		if ((cp = getenv("KRB5CCNAME")) != NULL)
106718de8d7fSPeter Avalos 			child_set_env(&env, &envsize, "KRB5CCNAME", cp);
106818de8d7fSPeter Avalos 	}
106918de8d7fSPeter Avalos 
107018de8d7fSPeter Avalos #ifdef _AIX
107118de8d7fSPeter Avalos 	{
107218de8d7fSPeter Avalos 		char *cp;
107318de8d7fSPeter Avalos 
107418de8d7fSPeter Avalos 		if ((cp = getenv("AUTHSTATE")) != NULL)
107518de8d7fSPeter Avalos 			child_set_env(&env, &envsize, "AUTHSTATE", cp);
1076664f4763Szrj 		read_environment_file(&env, &envsize, "/etc/environment",
107750a69bb5SSascha Wildner 		    options.permit_user_env_allowlist);
107818de8d7fSPeter Avalos 	}
107918de8d7fSPeter Avalos #endif
108018de8d7fSPeter Avalos #ifdef KRB5
108118de8d7fSPeter Avalos 	if (s->authctxt->krb5_ccname)
108218de8d7fSPeter Avalos 		child_set_env(&env, &envsize, "KRB5CCNAME",
108318de8d7fSPeter Avalos 		    s->authctxt->krb5_ccname);
108418de8d7fSPeter Avalos #endif
1085664f4763Szrj 	if (auth_sock_name != NULL)
1086664f4763Szrj 		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
1087664f4763Szrj 		    auth_sock_name);
1088664f4763Szrj 
1089664f4763Szrj 
1090664f4763Szrj 	/* Set custom environment options from pubkey authentication. */
1091664f4763Szrj 	if (options.permit_user_env) {
1092664f4763Szrj 		for (n = 0 ; n < auth_opts->nenv; n++) {
1093664f4763Szrj 			ocp = xstrdup(auth_opts->env[n]);
1094664f4763Szrj 			cp = strchr(ocp, '=');
109550a69bb5SSascha Wildner 			if (cp != NULL) {
1096664f4763Szrj 				*cp = '\0';
109750a69bb5SSascha Wildner 				/* Apply PermitUserEnvironment allowlist */
109850a69bb5SSascha Wildner 				if (options.permit_user_env_allowlist == NULL ||
1099664f4763Szrj 				    match_pattern_list(ocp,
110050a69bb5SSascha Wildner 				    options.permit_user_env_allowlist, 0) == 1)
1101664f4763Szrj 					child_set_env(&env, &envsize,
1102664f4763Szrj 					    ocp, cp + 1);
1103664f4763Szrj 			}
1104664f4763Szrj 			free(ocp);
1105664f4763Szrj 		}
1106664f4763Szrj 	}
1107664f4763Szrj 
1108664f4763Szrj 	/* read $HOME/.ssh/environment. */
1109664f4763Szrj 	if (options.permit_user_env) {
111050a69bb5SSascha Wildner 		snprintf(buf, sizeof buf, "%.200s/%s/environment",
111150a69bb5SSascha Wildner 		    pw->pw_dir, _PATH_SSH_USER_DIR);
1112664f4763Szrj 		read_environment_file(&env, &envsize, buf,
111350a69bb5SSascha Wildner 		    options.permit_user_env_allowlist);
1114664f4763Szrj 	}
1115664f4763Szrj 
111618de8d7fSPeter Avalos #ifdef USE_PAM
111718de8d7fSPeter Avalos 	/*
111818de8d7fSPeter Avalos 	 * Pull in any environment variables that may have
111918de8d7fSPeter Avalos 	 * been set by PAM.
112018de8d7fSPeter Avalos 	 */
11212c9c1408SMatthew Dillon 	if (options.use_pam) {
112218de8d7fSPeter Avalos 		char **p;
112318de8d7fSPeter Avalos 
11242c9c1408SMatthew Dillon 		/*
1125664f4763Szrj 		 * Don't allow PAM-internal env vars to leak
1126664f4763Szrj 		 * back into the session environment.
11272c9c1408SMatthew Dillon 		 */
112850a69bb5SSascha Wildner #define PAM_ENV_DENYLIST  "SSH_AUTH_INFO*,SSH_CONNECTION*"
112918de8d7fSPeter Avalos 		p = fetch_pam_child_environment();
113050a69bb5SSascha Wildner 		copy_environment_denylist(p, &env, &envsize,
113150a69bb5SSascha Wildner 		    PAM_ENV_DENYLIST);
113218de8d7fSPeter Avalos 		free_pam_environment(p);
113318de8d7fSPeter Avalos 
113418de8d7fSPeter Avalos 		p = fetch_pam_environment();
113550a69bb5SSascha Wildner 		copy_environment_denylist(p, &env, &envsize,
113650a69bb5SSascha Wildner 		    PAM_ENV_DENYLIST);
113718de8d7fSPeter Avalos 		free_pam_environment(p);
113818de8d7fSPeter Avalos 	}
113918de8d7fSPeter Avalos #endif /* USE_PAM */
114018de8d7fSPeter Avalos 
1141664f4763Szrj 	/* Environment specified by admin */
1142664f4763Szrj 	for (i = 0; i < options.num_setenv; i++) {
1143664f4763Szrj 		cp = xstrdup(options.setenv[i]);
1144664f4763Szrj 		if ((value = strchr(cp, '=')) == NULL) {
1145664f4763Szrj 			/* shouldn't happen; vars are checked in servconf.c */
1146664f4763Szrj 			fatal("Invalid config SetEnv: %s", options.setenv[i]);
114718de8d7fSPeter Avalos 		}
1148664f4763Szrj 		*value++ = '\0';
1149664f4763Szrj 		child_set_env(&env, &envsize, cp, value);
1150*ba1276acSMatthew Dillon 		free(cp);
1151664f4763Szrj 	}
1152664f4763Szrj 
1153664f4763Szrj 	/* SSH_CLIENT deprecated */
1154664f4763Szrj 	snprintf(buf, sizeof buf, "%.50s %d %d",
1155664f4763Szrj 	    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1156664f4763Szrj 	    ssh_local_port(ssh));
1157664f4763Szrj 	child_set_env(&env, &envsize, "SSH_CLIENT", buf);
1158664f4763Szrj 
1159664f4763Szrj 	laddr = get_local_ipaddr(ssh_packet_get_connection_in(ssh));
1160664f4763Szrj 	snprintf(buf, sizeof buf, "%.50s %d %.50s %d",
1161664f4763Szrj 	    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
1162664f4763Szrj 	    laddr, ssh_local_port(ssh));
1163664f4763Szrj 	free(laddr);
1164664f4763Szrj 	child_set_env(&env, &envsize, "SSH_CONNECTION", buf);
1165664f4763Szrj 
1166664f4763Szrj 	if (tun_fwd_ifnames != NULL)
1167664f4763Szrj 		child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames);
1168664f4763Szrj 	if (auth_info_file != NULL)
1169664f4763Szrj 		child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
1170664f4763Szrj 	if (s->ttyfd != -1)
1171664f4763Szrj 		child_set_env(&env, &envsize, "SSH_TTY", s->tty);
1172664f4763Szrj 	if (original_command)
1173664f4763Szrj 		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
1174664f4763Szrj 		    original_command);
1175664f4763Szrj 
117618de8d7fSPeter Avalos 	if (debug_flag) {
117718de8d7fSPeter Avalos 		/* dump the environment */
117818de8d7fSPeter Avalos 		fprintf(stderr, "Environment:\n");
117918de8d7fSPeter Avalos 		for (i = 0; env[i]; i++)
118018de8d7fSPeter Avalos 			fprintf(stderr, "  %.200s\n", env[i]);
118118de8d7fSPeter Avalos 	}
118218de8d7fSPeter Avalos 	return env;
118318de8d7fSPeter Avalos }
118418de8d7fSPeter Avalos 
118518de8d7fSPeter Avalos /*
118618de8d7fSPeter Avalos  * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found
118718de8d7fSPeter Avalos  * first in this order).
118818de8d7fSPeter Avalos  */
118918de8d7fSPeter Avalos static void
do_rc_files(struct ssh * ssh,Session * s,const char * shell)1190664f4763Szrj do_rc_files(struct ssh *ssh, Session *s, const char *shell)
119118de8d7fSPeter Avalos {
119218de8d7fSPeter Avalos 	FILE *f = NULL;
119350a69bb5SSascha Wildner 	char *cmd = NULL, *user_rc = NULL;
119418de8d7fSPeter Avalos 	int do_xauth;
119518de8d7fSPeter Avalos 	struct stat st;
119618de8d7fSPeter Avalos 
119718de8d7fSPeter Avalos 	do_xauth =
119818de8d7fSPeter Avalos 	    s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
119950a69bb5SSascha Wildner 	xasprintf(&user_rc, "%s/%s", s->pw->pw_dir, _PATH_SSH_USER_RC);
120018de8d7fSPeter Avalos 
120118de8d7fSPeter Avalos 	/* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
120218de8d7fSPeter Avalos 	if (!s->is_subsystem && options.adm_forced_command == NULL &&
1203664f4763Szrj 	    auth_opts->permit_user_rc && options.permit_user_rc &&
120450a69bb5SSascha Wildner 	    stat(user_rc, &st) >= 0) {
120550a69bb5SSascha Wildner 		if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL,
120650a69bb5SSascha Wildner 		    user_rc) == -1)
120750a69bb5SSascha Wildner 			fatal_f("xasprintf: %s", strerror(errno));
120818de8d7fSPeter Avalos 		if (debug_flag)
120918de8d7fSPeter Avalos 			fprintf(stderr, "Running %s\n", cmd);
121018de8d7fSPeter Avalos 		f = popen(cmd, "w");
121118de8d7fSPeter Avalos 		if (f) {
121218de8d7fSPeter Avalos 			if (do_xauth)
121318de8d7fSPeter Avalos 				fprintf(f, "%s %s\n", s->auth_proto,
121418de8d7fSPeter Avalos 				    s->auth_data);
121518de8d7fSPeter Avalos 			pclose(f);
121618de8d7fSPeter Avalos 		} else
121718de8d7fSPeter Avalos 			fprintf(stderr, "Could not run %s\n",
121850a69bb5SSascha Wildner 			    user_rc);
121918de8d7fSPeter Avalos 	} else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
122018de8d7fSPeter Avalos 		if (debug_flag)
122118de8d7fSPeter Avalos 			fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
122218de8d7fSPeter Avalos 			    _PATH_SSH_SYSTEM_RC);
122318de8d7fSPeter Avalos 		f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
122418de8d7fSPeter Avalos 		if (f) {
122518de8d7fSPeter Avalos 			if (do_xauth)
122618de8d7fSPeter Avalos 				fprintf(f, "%s %s\n", s->auth_proto,
122718de8d7fSPeter Avalos 				    s->auth_data);
122818de8d7fSPeter Avalos 			pclose(f);
122918de8d7fSPeter Avalos 		} else
123018de8d7fSPeter Avalos 			fprintf(stderr, "Could not run %s\n",
123118de8d7fSPeter Avalos 			    _PATH_SSH_SYSTEM_RC);
123218de8d7fSPeter Avalos 	} else if (do_xauth && options.xauth_location != NULL) {
123318de8d7fSPeter Avalos 		/* Add authority data to .Xauthority if appropriate. */
123418de8d7fSPeter Avalos 		if (debug_flag) {
123518de8d7fSPeter Avalos 			fprintf(stderr,
123618de8d7fSPeter Avalos 			    "Running %.500s remove %.100s\n",
123718de8d7fSPeter Avalos 			    options.xauth_location, s->auth_display);
123818de8d7fSPeter Avalos 			fprintf(stderr,
123918de8d7fSPeter Avalos 			    "%.500s add %.100s %.100s %.100s\n",
124018de8d7fSPeter Avalos 			    options.xauth_location, s->auth_display,
124118de8d7fSPeter Avalos 			    s->auth_proto, s->auth_data);
124218de8d7fSPeter Avalos 		}
124350a69bb5SSascha Wildner 		if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1)
124450a69bb5SSascha Wildner 			fatal_f("xasprintf: %s", strerror(errno));
124518de8d7fSPeter Avalos 		f = popen(cmd, "w");
124618de8d7fSPeter Avalos 		if (f) {
124718de8d7fSPeter Avalos 			fprintf(f, "remove %s\n",
124818de8d7fSPeter Avalos 			    s->auth_display);
124918de8d7fSPeter Avalos 			fprintf(f, "add %s %s %s\n",
125018de8d7fSPeter Avalos 			    s->auth_display, s->auth_proto,
125118de8d7fSPeter Avalos 			    s->auth_data);
125218de8d7fSPeter Avalos 			pclose(f);
125318de8d7fSPeter Avalos 		} else {
125418de8d7fSPeter Avalos 			fprintf(stderr, "Could not run %s\n",
125518de8d7fSPeter Avalos 			    cmd);
125618de8d7fSPeter Avalos 		}
125718de8d7fSPeter Avalos 	}
125850a69bb5SSascha Wildner 	free(cmd);
125950a69bb5SSascha Wildner 	free(user_rc);
126018de8d7fSPeter Avalos }
126118de8d7fSPeter Avalos 
126218de8d7fSPeter Avalos static void
do_nologin(struct passwd * pw)126318de8d7fSPeter Avalos do_nologin(struct passwd *pw)
126418de8d7fSPeter Avalos {
126518de8d7fSPeter Avalos 	FILE *f = NULL;
1266*ba1276acSMatthew Dillon 	char buf[1024], *def_nl = _PATH_NOLOGIN;
1267856ea928SPeter Avalos 	struct stat sb;
1268*ba1276acSMatthew Dillon 	const char *nl;
126918de8d7fSPeter Avalos 
127018de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
127199e85e0dSPeter Avalos 	if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0)
1272856ea928SPeter Avalos 		return;
1273*ba1276acSMatthew Dillon 	nl = login_getcapstr(lc, "nologin", def_nl, def_nl);
127418de8d7fSPeter Avalos #else
1275856ea928SPeter Avalos 	if (pw->pw_uid == 0)
1276856ea928SPeter Avalos 		return;
1277856ea928SPeter Avalos 	nl = def_nl;
127818de8d7fSPeter Avalos #endif
127950a69bb5SSascha Wildner 	if (stat(nl, &sb) == -1)
1280856ea928SPeter Avalos 		return;
1281856ea928SPeter Avalos 
1282856ea928SPeter Avalos 	/* /etc/nologin exists.  Print its contents if we can and exit. */
1283856ea928SPeter Avalos 	logit("User %.100s not allowed because %s exists", pw->pw_name, nl);
1284856ea928SPeter Avalos 	if ((f = fopen(nl, "r")) != NULL) {
128518de8d7fSPeter Avalos 		while (fgets(buf, sizeof(buf), f))
128618de8d7fSPeter Avalos 			fputs(buf, stderr);
128718de8d7fSPeter Avalos 		fclose(f);
128818de8d7fSPeter Avalos 	}
1289856ea928SPeter Avalos 	exit(254);
129018de8d7fSPeter Avalos }
129118de8d7fSPeter Avalos 
129218de8d7fSPeter Avalos /*
129318de8d7fSPeter Avalos  * Chroot into a directory after checking it for safety: all path components
129418de8d7fSPeter Avalos  * must be root-owned directories with strict permissions.
129518de8d7fSPeter Avalos  */
129618de8d7fSPeter Avalos static void
safely_chroot(const char * path,uid_t uid)129718de8d7fSPeter Avalos safely_chroot(const char *path, uid_t uid)
129818de8d7fSPeter Avalos {
129918de8d7fSPeter Avalos 	const char *cp;
1300e9778795SPeter Avalos 	char component[PATH_MAX];
130118de8d7fSPeter Avalos 	struct stat st;
130218de8d7fSPeter Avalos 
1303664f4763Szrj 	if (!path_absolute(path))
130418de8d7fSPeter Avalos 		fatal("chroot path does not begin at root");
130518de8d7fSPeter Avalos 	if (strlen(path) >= sizeof(component))
130618de8d7fSPeter Avalos 		fatal("chroot path too long");
130718de8d7fSPeter Avalos 
130818de8d7fSPeter Avalos 	/*
130918de8d7fSPeter Avalos 	 * Descend the path, checking that each component is a
131018de8d7fSPeter Avalos 	 * root-owned directory with strict permissions.
131118de8d7fSPeter Avalos 	 */
131218de8d7fSPeter Avalos 	for (cp = path; cp != NULL;) {
131318de8d7fSPeter Avalos 		if ((cp = strchr(cp, '/')) == NULL)
131418de8d7fSPeter Avalos 			strlcpy(component, path, sizeof(component));
131518de8d7fSPeter Avalos 		else {
131618de8d7fSPeter Avalos 			cp++;
131718de8d7fSPeter Avalos 			memcpy(component, path, cp - path);
131818de8d7fSPeter Avalos 			component[cp - path] = '\0';
131918de8d7fSPeter Avalos 		}
132018de8d7fSPeter Avalos 
132150a69bb5SSascha Wildner 		debug3_f("checking '%s'", component);
132218de8d7fSPeter Avalos 
132318de8d7fSPeter Avalos 		if (stat(component, &st) != 0)
132450a69bb5SSascha Wildner 			fatal_f("stat(\"%s\"): %s",
132518de8d7fSPeter Avalos 			    component, strerror(errno));
132618de8d7fSPeter Avalos 		if (st.st_uid != 0 || (st.st_mode & 022) != 0)
132718de8d7fSPeter Avalos 			fatal("bad ownership or modes for chroot "
132818de8d7fSPeter Avalos 			    "directory %s\"%s\"",
132918de8d7fSPeter Avalos 			    cp == NULL ? "" : "component ", component);
133018de8d7fSPeter Avalos 		if (!S_ISDIR(st.st_mode))
133118de8d7fSPeter Avalos 			fatal("chroot path %s\"%s\" is not a directory",
133218de8d7fSPeter Avalos 			    cp == NULL ? "" : "component ", component);
133318de8d7fSPeter Avalos 
133418de8d7fSPeter Avalos 	}
133518de8d7fSPeter Avalos 
133618de8d7fSPeter Avalos 	if (chdir(path) == -1)
133718de8d7fSPeter Avalos 		fatal("Unable to chdir to chroot path \"%s\": "
133818de8d7fSPeter Avalos 		    "%s", path, strerror(errno));
133918de8d7fSPeter Avalos 	if (chroot(path) == -1)
134018de8d7fSPeter Avalos 		fatal("chroot(\"%s\"): %s", path, strerror(errno));
134118de8d7fSPeter Avalos 	if (chdir("/") == -1)
134250a69bb5SSascha Wildner 		fatal_f("chdir(/) after chroot: %s", strerror(errno));
134318de8d7fSPeter Avalos 	verbose("Changed root directory to \"%s\"", path);
134418de8d7fSPeter Avalos }
134518de8d7fSPeter Avalos 
134618de8d7fSPeter Avalos /* Set login name, uid, gid, and groups. */
134718de8d7fSPeter Avalos void
do_setusercontext(struct passwd * pw)134818de8d7fSPeter Avalos do_setusercontext(struct passwd *pw)
134918de8d7fSPeter Avalos {
1350664f4763Szrj 	char uidstr[32], *chroot_path, *tmp;
135118de8d7fSPeter Avalos 
13529f304aafSPeter Avalos 	platform_setusercontext(pw);
135318de8d7fSPeter Avalos 
13549f304aafSPeter Avalos 	if (platform_privileged_uidswap()) {
135518de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
135618de8d7fSPeter Avalos 		if (setusercontext(lc, pw, pw->pw_uid,
135718de8d7fSPeter Avalos 		    (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) {
135818de8d7fSPeter Avalos 			perror("unable to set user context");
135918de8d7fSPeter Avalos 			exit(1);
136018de8d7fSPeter Avalos 		}
136118de8d7fSPeter Avalos #else
136218de8d7fSPeter Avalos 		if (setlogin(pw->pw_name) < 0)
136318de8d7fSPeter Avalos 			error("setlogin failed: %s", strerror(errno));
136418de8d7fSPeter Avalos 		if (setgid(pw->pw_gid) < 0) {
136518de8d7fSPeter Avalos 			perror("setgid");
136618de8d7fSPeter Avalos 			exit(1);
136718de8d7fSPeter Avalos 		}
136818de8d7fSPeter Avalos 		/* Initialize the group list. */
136918de8d7fSPeter Avalos 		if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
137018de8d7fSPeter Avalos 			perror("initgroups");
137118de8d7fSPeter Avalos 			exit(1);
137218de8d7fSPeter Avalos 		}
137318de8d7fSPeter Avalos 		endgrent();
137418de8d7fSPeter Avalos #endif
1375856ea928SPeter Avalos 
13769f304aafSPeter Avalos 		platform_setusercontext_post_groups(pw);
137718de8d7fSPeter Avalos 
1378e9778795SPeter Avalos 		if (!in_chroot && options.chroot_directory != NULL &&
137918de8d7fSPeter Avalos 		    strcasecmp(options.chroot_directory, "none") != 0) {
138018de8d7fSPeter Avalos 			tmp = tilde_expand_filename(options.chroot_directory,
138118de8d7fSPeter Avalos 			    pw->pw_uid);
1382664f4763Szrj 			snprintf(uidstr, sizeof(uidstr), "%llu",
1383664f4763Szrj 			    (unsigned long long)pw->pw_uid);
138418de8d7fSPeter Avalos 			chroot_path = percent_expand(tmp, "h", pw->pw_dir,
1385664f4763Szrj 			    "u", pw->pw_name, "U", uidstr, (char *)NULL);
138618de8d7fSPeter Avalos 			safely_chroot(chroot_path, pw->pw_uid);
138718de8d7fSPeter Avalos 			free(tmp);
138818de8d7fSPeter Avalos 			free(chroot_path);
138936e94dc5SPeter Avalos 			/* Make sure we don't attempt to chroot again */
139036e94dc5SPeter Avalos 			free(options.chroot_directory);
139136e94dc5SPeter Avalos 			options.chroot_directory = NULL;
1392e9778795SPeter Avalos 			in_chroot = 1;
139318de8d7fSPeter Avalos 		}
139418de8d7fSPeter Avalos 
139518de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
139618de8d7fSPeter Avalos 		if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) {
139718de8d7fSPeter Avalos 			perror("unable to set user context (setuser)");
139818de8d7fSPeter Avalos 			exit(1);
139918de8d7fSPeter Avalos 		}
140036e94dc5SPeter Avalos 		/*
140136e94dc5SPeter Avalos 		 * FreeBSD's setusercontext() will not apply the user's
140236e94dc5SPeter Avalos 		 * own umask setting unless running with the user's UID.
140336e94dc5SPeter Avalos 		 */
140436e94dc5SPeter Avalos 		(void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK);
140518de8d7fSPeter Avalos #else
140636e94dc5SPeter Avalos # ifdef USE_LIBIAF
1407e9778795SPeter Avalos 		/*
1408e9778795SPeter Avalos 		 * In a chroot environment, the set_id() will always fail;
1409e9778795SPeter Avalos 		 * typically because of the lack of necessary authentication
1410e9778795SPeter Avalos 		 * services and runtime such as ./usr/lib/libiaf.so,
1411e9778795SPeter Avalos 		 * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the
1412e9778795SPeter Avalos 		 * internal sftp chroot case.  We'll lose auditing and ACLs but
1413e9778795SPeter Avalos 		 * permanently_set_uid will take care of the rest.
141436e94dc5SPeter Avalos 		 */
1415e9778795SPeter Avalos 		if (!in_chroot && set_id(pw->pw_name) != 0)
141636e94dc5SPeter Avalos 			fatal("set_id(%s) Failed", pw->pw_name);
141736e94dc5SPeter Avalos # endif /* USE_LIBIAF */
141818de8d7fSPeter Avalos 		/* Permanently switch to the desired uid. */
141918de8d7fSPeter Avalos 		permanently_set_uid(pw);
142018de8d7fSPeter Avalos #endif
142136e94dc5SPeter Avalos 	} else if (options.chroot_directory != NULL &&
142236e94dc5SPeter Avalos 	    strcasecmp(options.chroot_directory, "none") != 0) {
142336e94dc5SPeter Avalos 		fatal("server lacks privileges to chroot to ChrootDirectory");
142418de8d7fSPeter Avalos 	}
142518de8d7fSPeter Avalos 
142618de8d7fSPeter Avalos 	if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
142718de8d7fSPeter Avalos 		fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
142818de8d7fSPeter Avalos }
142918de8d7fSPeter Avalos 
143018de8d7fSPeter Avalos static void
do_pwchange(Session * s)143118de8d7fSPeter Avalos do_pwchange(Session *s)
143218de8d7fSPeter Avalos {
143318de8d7fSPeter Avalos 	fflush(NULL);
143418de8d7fSPeter Avalos 	fprintf(stderr, "WARNING: Your password has expired.\n");
143518de8d7fSPeter Avalos 	if (s->ttyfd != -1) {
143618de8d7fSPeter Avalos 		fprintf(stderr,
143718de8d7fSPeter Avalos 		    "You must change your password now and login again!\n");
14381c188a7fSPeter Avalos #ifdef WITH_SELINUX
14391c188a7fSPeter Avalos 		setexeccon(NULL);
14401c188a7fSPeter Avalos #endif
144118de8d7fSPeter Avalos #ifdef PASSWD_NEEDS_USERNAME
144218de8d7fSPeter Avalos 		execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name,
144318de8d7fSPeter Avalos 		    (char *)NULL);
144418de8d7fSPeter Avalos #else
144518de8d7fSPeter Avalos 		execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL);
144618de8d7fSPeter Avalos #endif
144718de8d7fSPeter Avalos 		perror("passwd");
144818de8d7fSPeter Avalos 	} else {
144918de8d7fSPeter Avalos 		fprintf(stderr,
145018de8d7fSPeter Avalos 		    "Password change required but no TTY available.\n");
145118de8d7fSPeter Avalos 	}
145218de8d7fSPeter Avalos 	exit(1);
145318de8d7fSPeter Avalos }
145418de8d7fSPeter Avalos 
145518de8d7fSPeter Avalos static void
child_close_fds(struct ssh * ssh)14562c9c1408SMatthew Dillon child_close_fds(struct ssh *ssh)
145718de8d7fSPeter Avalos {
1458e9778795SPeter Avalos 	extern int auth_sock;
145936e94dc5SPeter Avalos 
1460e9778795SPeter Avalos 	if (auth_sock != -1) {
1461e9778795SPeter Avalos 		close(auth_sock);
1462e9778795SPeter Avalos 		auth_sock = -1;
146336e94dc5SPeter Avalos 	}
146436e94dc5SPeter Avalos 
1465664f4763Szrj 	if (ssh_packet_get_connection_in(ssh) ==
1466664f4763Szrj 	    ssh_packet_get_connection_out(ssh))
1467664f4763Szrj 		close(ssh_packet_get_connection_in(ssh));
146818de8d7fSPeter Avalos 	else {
1469664f4763Szrj 		close(ssh_packet_get_connection_in(ssh));
1470664f4763Szrj 		close(ssh_packet_get_connection_out(ssh));
147118de8d7fSPeter Avalos 	}
147218de8d7fSPeter Avalos 	/*
147318de8d7fSPeter Avalos 	 * Close all descriptors related to channels.  They will still remain
147418de8d7fSPeter Avalos 	 * open in the parent.
147518de8d7fSPeter Avalos 	 */
147618de8d7fSPeter Avalos 	/* XXX better use close-on-exec? -markus */
14772c9c1408SMatthew Dillon 	channel_close_all(ssh);
147818de8d7fSPeter Avalos 
147918de8d7fSPeter Avalos 	/*
148018de8d7fSPeter Avalos 	 * Close any extra file descriptors.  Note that there may still be
148118de8d7fSPeter Avalos 	 * descriptors left by system functions.  They will be closed later.
148218de8d7fSPeter Avalos 	 */
148318de8d7fSPeter Avalos 	endpwent();
148418de8d7fSPeter Avalos 
148550a69bb5SSascha Wildner 	/* Stop directing logs to a high-numbered fd before we close it */
148650a69bb5SSascha Wildner 	log_redirect_stderr_to(NULL);
148750a69bb5SSascha Wildner 
148818de8d7fSPeter Avalos 	/*
148918de8d7fSPeter Avalos 	 * Close any extra open file descriptors so that we don't have them
149018de8d7fSPeter Avalos 	 * hanging around in clients.  Note that we want to do this after
149118de8d7fSPeter Avalos 	 * initgroups, because at least on Solaris 2.3 it leaves file
149218de8d7fSPeter Avalos 	 * descriptors open.
149318de8d7fSPeter Avalos 	 */
14949f304aafSPeter Avalos 	closefrom(STDERR_FILENO + 1);
149518de8d7fSPeter Avalos }
149618de8d7fSPeter Avalos 
149718de8d7fSPeter Avalos /*
149818de8d7fSPeter Avalos  * Performs common processing for the child, such as setting up the
149918de8d7fSPeter Avalos  * environment, closing extra file descriptors, setting the user and group
150018de8d7fSPeter Avalos  * ids, and executing the command or shell.
150118de8d7fSPeter Avalos  */
150218de8d7fSPeter Avalos #define ARGV_MAX 10
150318de8d7fSPeter Avalos void
do_child(struct ssh * ssh,Session * s,const char * command)15042c9c1408SMatthew Dillon do_child(struct ssh *ssh, Session *s, const char *command)
150518de8d7fSPeter Avalos {
150618de8d7fSPeter Avalos 	extern char **environ;
1507664f4763Szrj 	char **env, *argv[ARGV_MAX], remote_id[512];
15082c9c1408SMatthew Dillon 	const char *shell, *shell0;
150918de8d7fSPeter Avalos 	struct passwd *pw = s->pw;
151018de8d7fSPeter Avalos 	int r = 0;
151118de8d7fSPeter Avalos 
1512664f4763Szrj 	sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
1513664f4763Szrj 
151418de8d7fSPeter Avalos 	/* remove hostkey from the child's memory */
151518de8d7fSPeter Avalos 	destroy_sensitive_data();
1516664f4763Szrj 	ssh_packet_clear_keys(ssh);
151718de8d7fSPeter Avalos 
151818de8d7fSPeter Avalos 	/* Force a password change */
151918de8d7fSPeter Avalos 	if (s->authctxt->force_pwchange) {
152018de8d7fSPeter Avalos 		do_setusercontext(pw);
15212c9c1408SMatthew Dillon 		child_close_fds(ssh);
152218de8d7fSPeter Avalos 		do_pwchange(s);
152318de8d7fSPeter Avalos 		exit(1);
152418de8d7fSPeter Avalos 	}
152518de8d7fSPeter Avalos 
152618de8d7fSPeter Avalos 	/*
152718de8d7fSPeter Avalos 	 * Login(1) does this as well, and it needs uid 0 for the "-h"
152818de8d7fSPeter Avalos 	 * switch, so we let login(1) to this for us.
152918de8d7fSPeter Avalos 	 */
153018de8d7fSPeter Avalos #ifdef HAVE_OSF_SIA
153118de8d7fSPeter Avalos 	session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty);
153218de8d7fSPeter Avalos 	if (!check_quietlogin(s, command))
153318de8d7fSPeter Avalos 		do_motd();
153418de8d7fSPeter Avalos #else /* HAVE_OSF_SIA */
153518de8d7fSPeter Avalos 	/* When PAM is enabled we rely on it to do the nologin check */
153618de8d7fSPeter Avalos 	if (!options.use_pam)
153718de8d7fSPeter Avalos 		do_nologin(pw);
153818de8d7fSPeter Avalos 	do_setusercontext(pw);
153918de8d7fSPeter Avalos 	/*
154018de8d7fSPeter Avalos 	 * PAM session modules in do_setusercontext may have
154118de8d7fSPeter Avalos 	 * generated messages, so if this in an interactive
154218de8d7fSPeter Avalos 	 * login then display them too.
154318de8d7fSPeter Avalos 	 */
154418de8d7fSPeter Avalos 	if (!check_quietlogin(s, command))
154518de8d7fSPeter Avalos 		display_loginmsg();
154618de8d7fSPeter Avalos #endif /* HAVE_OSF_SIA */
154718de8d7fSPeter Avalos 
154818de8d7fSPeter Avalos #ifdef USE_PAM
15492c9c1408SMatthew Dillon 	if (options.use_pam && !is_pam_session_open()) {
155018de8d7fSPeter Avalos 		debug3("PAM session not opened, exiting");
155118de8d7fSPeter Avalos 		display_loginmsg();
155218de8d7fSPeter Avalos 		exit(254);
155318de8d7fSPeter Avalos 	}
155418de8d7fSPeter Avalos #endif
155518de8d7fSPeter Avalos 
155618de8d7fSPeter Avalos 	/*
155718de8d7fSPeter Avalos 	 * Get the shell from the password data.  An empty shell field is
155818de8d7fSPeter Avalos 	 * legal, and means /bin/sh.
155918de8d7fSPeter Avalos 	 */
156018de8d7fSPeter Avalos 	shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
156118de8d7fSPeter Avalos 
156218de8d7fSPeter Avalos 	/*
156318de8d7fSPeter Avalos 	 * Make sure $SHELL points to the shell from the password file,
156418de8d7fSPeter Avalos 	 * even if shell is overridden from login.conf
156518de8d7fSPeter Avalos 	 */
15662c9c1408SMatthew Dillon 	env = do_setup_env(ssh, s, shell);
156718de8d7fSPeter Avalos 
156818de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
156918de8d7fSPeter Avalos 	shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
157018de8d7fSPeter Avalos #endif
157118de8d7fSPeter Avalos 
157218de8d7fSPeter Avalos 	/*
157318de8d7fSPeter Avalos 	 * Close the connection descriptors; note that this is the child, and
157418de8d7fSPeter Avalos 	 * the server will still have the socket open, and it is important
157518de8d7fSPeter Avalos 	 * that we do not shutdown it.  Note that the descriptors cannot be
157618de8d7fSPeter Avalos 	 * closed before building the environment, as we call
1577e9778795SPeter Avalos 	 * ssh_remote_ipaddr there.
157818de8d7fSPeter Avalos 	 */
15792c9c1408SMatthew Dillon 	child_close_fds(ssh);
158018de8d7fSPeter Avalos 
158118de8d7fSPeter Avalos 	/*
158218de8d7fSPeter Avalos 	 * Must take new environment into use so that .ssh/rc,
158318de8d7fSPeter Avalos 	 * /etc/ssh/sshrc and xauth are run in the proper environment.
158418de8d7fSPeter Avalos 	 */
158518de8d7fSPeter Avalos 	environ = env;
158618de8d7fSPeter Avalos 
158718de8d7fSPeter Avalos #if defined(KRB5) && defined(USE_AFS)
158818de8d7fSPeter Avalos 	/*
158918de8d7fSPeter Avalos 	 * At this point, we check to see if AFS is active and if we have
159018de8d7fSPeter Avalos 	 * a valid Kerberos 5 TGT. If so, it seems like a good idea to see
159118de8d7fSPeter Avalos 	 * if we can (and need to) extend the ticket into an AFS token. If
159218de8d7fSPeter Avalos 	 * we don't do this, we run into potential problems if the user's
159318de8d7fSPeter Avalos 	 * home directory is in AFS and it's not world-readable.
159418de8d7fSPeter Avalos 	 */
159518de8d7fSPeter Avalos 
159618de8d7fSPeter Avalos 	if (options.kerberos_get_afs_token && k_hasafs() &&
159718de8d7fSPeter Avalos 	    (s->authctxt->krb5_ctx != NULL)) {
159818de8d7fSPeter Avalos 		char cell[64];
159918de8d7fSPeter Avalos 
160018de8d7fSPeter Avalos 		debug("Getting AFS token");
160118de8d7fSPeter Avalos 
160218de8d7fSPeter Avalos 		k_setpag();
160318de8d7fSPeter Avalos 
160418de8d7fSPeter Avalos 		if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
160518de8d7fSPeter Avalos 			krb5_afslog(s->authctxt->krb5_ctx,
160618de8d7fSPeter Avalos 			    s->authctxt->krb5_fwd_ccache, cell, NULL);
160718de8d7fSPeter Avalos 
160818de8d7fSPeter Avalos 		krb5_afslog_home(s->authctxt->krb5_ctx,
160918de8d7fSPeter Avalos 		    s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir);
161018de8d7fSPeter Avalos 	}
161118de8d7fSPeter Avalos #endif
161218de8d7fSPeter Avalos 
161318de8d7fSPeter Avalos 	/* Change current directory to the user's home directory. */
16140cbfa66cSDaniel Fojt 	if (chdir(pw->pw_dir) == -1) {
161518de8d7fSPeter Avalos 		/* Suppress missing homedir warning for chroot case */
161618de8d7fSPeter Avalos #ifdef HAVE_LOGIN_CAP
161718de8d7fSPeter Avalos 		r = login_getcapbool(lc, "requirehome", 0);
161818de8d7fSPeter Avalos #endif
1619e9778795SPeter Avalos 		if (r || !in_chroot) {
162018de8d7fSPeter Avalos 			fprintf(stderr, "Could not chdir to home "
162118de8d7fSPeter Avalos 			    "directory %s: %s\n", pw->pw_dir,
162218de8d7fSPeter Avalos 			    strerror(errno));
1623e9778795SPeter Avalos 		}
162418de8d7fSPeter Avalos 		if (r)
162518de8d7fSPeter Avalos 			exit(1);
162618de8d7fSPeter Avalos 	}
162718de8d7fSPeter Avalos 
162818de8d7fSPeter Avalos 	closefrom(STDERR_FILENO + 1);
162918de8d7fSPeter Avalos 
1630664f4763Szrj 	do_rc_files(ssh, s, shell);
163118de8d7fSPeter Avalos 
163218de8d7fSPeter Avalos 	/* restore SIGPIPE for child */
16330cbfa66cSDaniel Fojt 	ssh_signal(SIGPIPE, SIG_DFL);
163418de8d7fSPeter Avalos 
1635856ea928SPeter Avalos 	if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) {
1636664f4763Szrj 		error("Connection from %s: refusing non-sftp session",
1637664f4763Szrj 		    remote_id);
1638856ea928SPeter Avalos 		printf("This service allows sftp connections only.\n");
1639856ea928SPeter Avalos 		fflush(NULL);
1640856ea928SPeter Avalos 		exit(1);
1641856ea928SPeter Avalos 	} else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
164218de8d7fSPeter Avalos 		extern int optind, optreset;
164318de8d7fSPeter Avalos 		int i;
164418de8d7fSPeter Avalos 		char *p, *args;
164518de8d7fSPeter Avalos 
164640c002afSPeter Avalos 		setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME);
1647cb5eb4f1SPeter Avalos 		args = xstrdup(command ? command : "sftp-server");
164818de8d7fSPeter Avalos 		for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " ")))
164918de8d7fSPeter Avalos 			if (i < ARGV_MAX - 1)
165018de8d7fSPeter Avalos 				argv[i++] = p;
165118de8d7fSPeter Avalos 		argv[i] = NULL;
165218de8d7fSPeter Avalos 		optind = optreset = 1;
165318de8d7fSPeter Avalos 		__progname = argv[0];
1654856ea928SPeter Avalos #ifdef WITH_SELINUX
1655856ea928SPeter Avalos 		ssh_selinux_change_context("sftpd_t");
1656856ea928SPeter Avalos #endif
165718de8d7fSPeter Avalos 		exit(sftp_server_main(i, argv, s->pw));
165818de8d7fSPeter Avalos 	}
165918de8d7fSPeter Avalos 
1660856ea928SPeter Avalos 	fflush(NULL);
1661856ea928SPeter Avalos 
166218de8d7fSPeter Avalos 	/* Get the last component of the shell name. */
166318de8d7fSPeter Avalos 	if ((shell0 = strrchr(shell, '/')) != NULL)
166418de8d7fSPeter Avalos 		shell0++;
166518de8d7fSPeter Avalos 	else
166618de8d7fSPeter Avalos 		shell0 = shell;
166718de8d7fSPeter Avalos 
166818de8d7fSPeter Avalos 	/*
166918de8d7fSPeter Avalos 	 * If we have no command, execute the shell.  In this case, the shell
167018de8d7fSPeter Avalos 	 * name to be passed in argv[0] is preceded by '-' to indicate that
167118de8d7fSPeter Avalos 	 * this is a login shell.
167218de8d7fSPeter Avalos 	 */
167318de8d7fSPeter Avalos 	if (!command) {
167418de8d7fSPeter Avalos 		char argv0[256];
167518de8d7fSPeter Avalos 
167618de8d7fSPeter Avalos 		/* Start the shell.  Set initial character to '-'. */
167718de8d7fSPeter Avalos 		argv0[0] = '-';
167818de8d7fSPeter Avalos 
167918de8d7fSPeter Avalos 		if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1)
168018de8d7fSPeter Avalos 		    >= sizeof(argv0) - 1) {
168118de8d7fSPeter Avalos 			errno = EINVAL;
168218de8d7fSPeter Avalos 			perror(shell);
168318de8d7fSPeter Avalos 			exit(1);
168418de8d7fSPeter Avalos 		}
168518de8d7fSPeter Avalos 
168618de8d7fSPeter Avalos 		/* Execute the shell. */
168718de8d7fSPeter Avalos 		argv[0] = argv0;
168818de8d7fSPeter Avalos 		argv[1] = NULL;
168918de8d7fSPeter Avalos 		execve(shell, argv, env);
169018de8d7fSPeter Avalos 
169118de8d7fSPeter Avalos 		/* Executing the shell failed. */
169218de8d7fSPeter Avalos 		perror(shell);
169318de8d7fSPeter Avalos 		exit(1);
169418de8d7fSPeter Avalos 	}
169518de8d7fSPeter Avalos 	/*
169618de8d7fSPeter Avalos 	 * Execute the command using the user's shell.  This uses the -c
169718de8d7fSPeter Avalos 	 * option to execute the command.
169818de8d7fSPeter Avalos 	 */
169918de8d7fSPeter Avalos 	argv[0] = (char *) shell0;
170018de8d7fSPeter Avalos 	argv[1] = "-c";
170118de8d7fSPeter Avalos 	argv[2] = (char *) command;
170218de8d7fSPeter Avalos 	argv[3] = NULL;
170318de8d7fSPeter Avalos 	execve(shell, argv, env);
170418de8d7fSPeter Avalos 	perror(shell);
170518de8d7fSPeter Avalos 	exit(1);
170618de8d7fSPeter Avalos }
170718de8d7fSPeter Avalos 
170818de8d7fSPeter Avalos void
session_unused(int id)170918de8d7fSPeter Avalos session_unused(int id)
171018de8d7fSPeter Avalos {
171150a69bb5SSascha Wildner 	debug3_f("session id %d unused", id);
171218de8d7fSPeter Avalos 	if (id >= options.max_sessions ||
171318de8d7fSPeter Avalos 	    id >= sessions_nalloc) {
171450a69bb5SSascha Wildner 		fatal_f("insane session id %d (max %d nalloc %d)",
171550a69bb5SSascha Wildner 		    id, options.max_sessions, sessions_nalloc);
171618de8d7fSPeter Avalos 	}
171736e94dc5SPeter Avalos 	memset(&sessions[id], 0, sizeof(*sessions));
171818de8d7fSPeter Avalos 	sessions[id].self = id;
171918de8d7fSPeter Avalos 	sessions[id].used = 0;
172018de8d7fSPeter Avalos 	sessions[id].chanid = -1;
172118de8d7fSPeter Avalos 	sessions[id].ptyfd = -1;
172218de8d7fSPeter Avalos 	sessions[id].ttyfd = -1;
172318de8d7fSPeter Avalos 	sessions[id].ptymaster = -1;
172418de8d7fSPeter Avalos 	sessions[id].x11_chanids = NULL;
172518de8d7fSPeter Avalos 	sessions[id].next_unused = sessions_first_unused;
172618de8d7fSPeter Avalos 	sessions_first_unused = id;
172718de8d7fSPeter Avalos }
172818de8d7fSPeter Avalos 
172918de8d7fSPeter Avalos Session *
session_new(void)173018de8d7fSPeter Avalos session_new(void)
173118de8d7fSPeter Avalos {
173218de8d7fSPeter Avalos 	Session *s, *tmp;
173318de8d7fSPeter Avalos 
173418de8d7fSPeter Avalos 	if (sessions_first_unused == -1) {
173518de8d7fSPeter Avalos 		if (sessions_nalloc >= options.max_sessions)
173618de8d7fSPeter Avalos 			return NULL;
173750a69bb5SSascha Wildner 		debug2_f("allocate (allocated %d max %d)",
173850a69bb5SSascha Wildner 		    sessions_nalloc, options.max_sessions);
17392c9c1408SMatthew Dillon 		tmp = xrecallocarray(sessions, sessions_nalloc,
17402c9c1408SMatthew Dillon 		    sessions_nalloc + 1, sizeof(*sessions));
174118de8d7fSPeter Avalos 		if (tmp == NULL) {
174250a69bb5SSascha Wildner 			error_f("cannot allocate %d sessions",
174350a69bb5SSascha Wildner 			    sessions_nalloc + 1);
174418de8d7fSPeter Avalos 			return NULL;
174518de8d7fSPeter Avalos 		}
174618de8d7fSPeter Avalos 		sessions = tmp;
174718de8d7fSPeter Avalos 		session_unused(sessions_nalloc++);
174818de8d7fSPeter Avalos 	}
174918de8d7fSPeter Avalos 
175018de8d7fSPeter Avalos 	if (sessions_first_unused >= sessions_nalloc ||
175118de8d7fSPeter Avalos 	    sessions_first_unused < 0) {
175250a69bb5SSascha Wildner 		fatal_f("insane first_unused %d max %d nalloc %d",
175350a69bb5SSascha Wildner 		    sessions_first_unused, options.max_sessions,
175418de8d7fSPeter Avalos 		    sessions_nalloc);
175518de8d7fSPeter Avalos 	}
175618de8d7fSPeter Avalos 
175718de8d7fSPeter Avalos 	s = &sessions[sessions_first_unused];
175850a69bb5SSascha Wildner 	if (s->used)
175950a69bb5SSascha Wildner 		fatal_f("session %d already used", sessions_first_unused);
176018de8d7fSPeter Avalos 	sessions_first_unused = s->next_unused;
176118de8d7fSPeter Avalos 	s->used = 1;
176218de8d7fSPeter Avalos 	s->next_unused = -1;
176318de8d7fSPeter Avalos 	debug("session_new: session %d", s->self);
176418de8d7fSPeter Avalos 
176518de8d7fSPeter Avalos 	return s;
176618de8d7fSPeter Avalos }
176718de8d7fSPeter Avalos 
176818de8d7fSPeter Avalos static void
session_dump(void)176918de8d7fSPeter Avalos session_dump(void)
177018de8d7fSPeter Avalos {
177118de8d7fSPeter Avalos 	int i;
177218de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
177318de8d7fSPeter Avalos 		Session *s = &sessions[i];
177418de8d7fSPeter Avalos 
177550a69bb5SSascha Wildner 		debug("dump: used %d next_unused %d session %d "
177618de8d7fSPeter Avalos 		    "channel %d pid %ld",
177718de8d7fSPeter Avalos 		    s->used,
177818de8d7fSPeter Avalos 		    s->next_unused,
177918de8d7fSPeter Avalos 		    s->self,
178018de8d7fSPeter Avalos 		    s->chanid,
178118de8d7fSPeter Avalos 		    (long)s->pid);
178218de8d7fSPeter Avalos 	}
178318de8d7fSPeter Avalos }
178418de8d7fSPeter Avalos 
178518de8d7fSPeter Avalos int
session_open(Authctxt * authctxt,int chanid)178618de8d7fSPeter Avalos session_open(Authctxt *authctxt, int chanid)
178718de8d7fSPeter Avalos {
178818de8d7fSPeter Avalos 	Session *s = session_new();
178918de8d7fSPeter Avalos 	debug("session_open: channel %d", chanid);
179018de8d7fSPeter Avalos 	if (s == NULL) {
179118de8d7fSPeter Avalos 		error("no more sessions");
179218de8d7fSPeter Avalos 		return 0;
179318de8d7fSPeter Avalos 	}
179418de8d7fSPeter Avalos 	s->authctxt = authctxt;
179518de8d7fSPeter Avalos 	s->pw = authctxt->pw;
179618de8d7fSPeter Avalos 	if (s->pw == NULL || !authctxt->valid)
179718de8d7fSPeter Avalos 		fatal("no user for session %d", s->self);
179818de8d7fSPeter Avalos 	debug("session_open: session %d: link with channel %d", s->self, chanid);
179918de8d7fSPeter Avalos 	s->chanid = chanid;
180018de8d7fSPeter Avalos 	return 1;
180118de8d7fSPeter Avalos }
180218de8d7fSPeter Avalos 
180318de8d7fSPeter Avalos Session *
session_by_tty(char * tty)180418de8d7fSPeter Avalos session_by_tty(char *tty)
180518de8d7fSPeter Avalos {
180618de8d7fSPeter Avalos 	int i;
180718de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
180818de8d7fSPeter Avalos 		Session *s = &sessions[i];
180918de8d7fSPeter Avalos 		if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
181018de8d7fSPeter Avalos 			debug("session_by_tty: session %d tty %s", i, tty);
181118de8d7fSPeter Avalos 			return s;
181218de8d7fSPeter Avalos 		}
181318de8d7fSPeter Avalos 	}
181418de8d7fSPeter Avalos 	debug("session_by_tty: unknown tty %.100s", tty);
181518de8d7fSPeter Avalos 	session_dump();
181618de8d7fSPeter Avalos 	return NULL;
181718de8d7fSPeter Avalos }
181818de8d7fSPeter Avalos 
181918de8d7fSPeter Avalos static Session *
session_by_channel(int id)182018de8d7fSPeter Avalos session_by_channel(int id)
182118de8d7fSPeter Avalos {
182218de8d7fSPeter Avalos 	int i;
182318de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
182418de8d7fSPeter Avalos 		Session *s = &sessions[i];
182518de8d7fSPeter Avalos 		if (s->used && s->chanid == id) {
182618de8d7fSPeter Avalos 			debug("session_by_channel: session %d channel %d",
182718de8d7fSPeter Avalos 			    i, id);
182818de8d7fSPeter Avalos 			return s;
182918de8d7fSPeter Avalos 		}
183018de8d7fSPeter Avalos 	}
183118de8d7fSPeter Avalos 	debug("session_by_channel: unknown channel %d", id);
183218de8d7fSPeter Avalos 	session_dump();
183318de8d7fSPeter Avalos 	return NULL;
183418de8d7fSPeter Avalos }
183518de8d7fSPeter Avalos 
183618de8d7fSPeter Avalos static Session *
session_by_x11_channel(int id)183718de8d7fSPeter Avalos session_by_x11_channel(int id)
183818de8d7fSPeter Avalos {
183918de8d7fSPeter Avalos 	int i, j;
184018de8d7fSPeter Avalos 
184118de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
184218de8d7fSPeter Avalos 		Session *s = &sessions[i];
184318de8d7fSPeter Avalos 
184418de8d7fSPeter Avalos 		if (s->x11_chanids == NULL || !s->used)
184518de8d7fSPeter Avalos 			continue;
184618de8d7fSPeter Avalos 		for (j = 0; s->x11_chanids[j] != -1; j++) {
184718de8d7fSPeter Avalos 			if (s->x11_chanids[j] == id) {
184818de8d7fSPeter Avalos 				debug("session_by_x11_channel: session %d "
184918de8d7fSPeter Avalos 				    "channel %d", s->self, id);
185018de8d7fSPeter Avalos 				return s;
185118de8d7fSPeter Avalos 			}
185218de8d7fSPeter Avalos 		}
185318de8d7fSPeter Avalos 	}
185418de8d7fSPeter Avalos 	debug("session_by_x11_channel: unknown channel %d", id);
185518de8d7fSPeter Avalos 	session_dump();
185618de8d7fSPeter Avalos 	return NULL;
185718de8d7fSPeter Avalos }
185818de8d7fSPeter Avalos 
185918de8d7fSPeter Avalos static Session *
session_by_pid(pid_t pid)186018de8d7fSPeter Avalos session_by_pid(pid_t pid)
186118de8d7fSPeter Avalos {
186218de8d7fSPeter Avalos 	int i;
186318de8d7fSPeter Avalos 	debug("session_by_pid: pid %ld", (long)pid);
186418de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
186518de8d7fSPeter Avalos 		Session *s = &sessions[i];
186618de8d7fSPeter Avalos 		if (s->used && s->pid == pid)
186718de8d7fSPeter Avalos 			return s;
186818de8d7fSPeter Avalos 	}
186918de8d7fSPeter Avalos 	error("session_by_pid: unknown pid %ld", (long)pid);
187018de8d7fSPeter Avalos 	session_dump();
187118de8d7fSPeter Avalos 	return NULL;
187218de8d7fSPeter Avalos }
187318de8d7fSPeter Avalos 
187418de8d7fSPeter Avalos static int
session_window_change_req(struct ssh * ssh,Session * s)18752c9c1408SMatthew Dillon session_window_change_req(struct ssh *ssh, Session *s)
187618de8d7fSPeter Avalos {
1877664f4763Szrj 	int r;
1878664f4763Szrj 
1879664f4763Szrj 	if ((r = sshpkt_get_u32(ssh, &s->col)) != 0 ||
1880664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->row)) != 0 ||
1881664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 ||
1882664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0 ||
1883664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
1884664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
188518de8d7fSPeter Avalos 	pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
188618de8d7fSPeter Avalos 	return 1;
188718de8d7fSPeter Avalos }
188818de8d7fSPeter Avalos 
188918de8d7fSPeter Avalos static int
session_pty_req(struct ssh * ssh,Session * s)18902c9c1408SMatthew Dillon session_pty_req(struct ssh *ssh, Session *s)
189118de8d7fSPeter Avalos {
1892664f4763Szrj 	int r;
189318de8d7fSPeter Avalos 
1894664f4763Szrj 	if (!auth_opts->permit_pty_flag || !options.permit_tty) {
1895664f4763Szrj 		debug("Allocating a pty not permitted for this connection.");
189618de8d7fSPeter Avalos 		return 0;
189718de8d7fSPeter Avalos 	}
189818de8d7fSPeter Avalos 	if (s->ttyfd != -1) {
1899664f4763Szrj 		ssh_packet_disconnect(ssh, "Protocol error: you already have a pty.");
190018de8d7fSPeter Avalos 		return 0;
190118de8d7fSPeter Avalos 	}
190218de8d7fSPeter Avalos 
1903664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &s->term, NULL)) != 0 ||
1904664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->col)) != 0 ||
1905664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->row)) != 0 ||
1906664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 ||
1907664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0)
1908664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
190918de8d7fSPeter Avalos 
191018de8d7fSPeter Avalos 	if (strcmp(s->term, "") == 0) {
191136e94dc5SPeter Avalos 		free(s->term);
191218de8d7fSPeter Avalos 		s->term = NULL;
191318de8d7fSPeter Avalos 	}
191418de8d7fSPeter Avalos 
191518de8d7fSPeter Avalos 	/* Allocate a pty and open it. */
191618de8d7fSPeter Avalos 	debug("Allocating pty.");
1917*ba1276acSMatthew Dillon 	if (!mm_pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
191836e94dc5SPeter Avalos 		free(s->term);
191918de8d7fSPeter Avalos 		s->term = NULL;
192018de8d7fSPeter Avalos 		s->ptyfd = -1;
192118de8d7fSPeter Avalos 		s->ttyfd = -1;
192218de8d7fSPeter Avalos 		error("session_pty_req: session %d alloc failed", s->self);
192318de8d7fSPeter Avalos 		return 0;
192418de8d7fSPeter Avalos 	}
192518de8d7fSPeter Avalos 	debug("session_pty_req: session %d alloc %s", s->self, s->tty);
192618de8d7fSPeter Avalos 
1927664f4763Szrj 	ssh_tty_parse_modes(ssh, s->ttyfd);
1928664f4763Szrj 
1929664f4763Szrj 	if ((r = sshpkt_get_end(ssh)) != 0)
1930664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
193118de8d7fSPeter Avalos 
193218de8d7fSPeter Avalos 	/* Set window size from the packet. */
193318de8d7fSPeter Avalos 	pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
193418de8d7fSPeter Avalos 
193518de8d7fSPeter Avalos 	session_proctitle(s);
193618de8d7fSPeter Avalos 	return 1;
193718de8d7fSPeter Avalos }
193818de8d7fSPeter Avalos 
193918de8d7fSPeter Avalos static int
session_subsystem_req(struct ssh * ssh,Session * s)19402c9c1408SMatthew Dillon session_subsystem_req(struct ssh *ssh, Session *s)
194118de8d7fSPeter Avalos {
194218de8d7fSPeter Avalos 	struct stat st;
1943664f4763Szrj 	int r, success = 0;
1944*ba1276acSMatthew Dillon 	char *prog, *cmd, *type;
194518de8d7fSPeter Avalos 	u_int i;
194618de8d7fSPeter Avalos 
1947664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &s->subsys, NULL)) != 0 ||
1948664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
1949664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
195036e94dc5SPeter Avalos 	debug2("subsystem request for %.100s by user %s", s->subsys,
1951856ea928SPeter Avalos 	    s->pw->pw_name);
195218de8d7fSPeter Avalos 
195318de8d7fSPeter Avalos 	for (i = 0; i < options.num_subsystems; i++) {
195436e94dc5SPeter Avalos 		if (strcmp(s->subsys, options.subsystem_name[i]) == 0) {
195518de8d7fSPeter Avalos 			prog = options.subsystem_command[i];
195618de8d7fSPeter Avalos 			cmd = options.subsystem_args[i];
1957856ea928SPeter Avalos 			if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) {
195818de8d7fSPeter Avalos 				s->is_subsystem = SUBSYSTEM_INT_SFTP;
1959856ea928SPeter Avalos 				debug("subsystem: %s", prog);
196018de8d7fSPeter Avalos 			} else {
19610cbfa66cSDaniel Fojt 				if (stat(prog, &st) == -1)
1962856ea928SPeter Avalos 					debug("subsystem: cannot stat %s: %s",
1963856ea928SPeter Avalos 					    prog, strerror(errno));
196418de8d7fSPeter Avalos 				s->is_subsystem = SUBSYSTEM_EXT;
196518de8d7fSPeter Avalos 				debug("subsystem: exec() %s", cmd);
1966856ea928SPeter Avalos 			}
1967*ba1276acSMatthew Dillon 			xasprintf(&type, "session:subsystem:%s",
1968*ba1276acSMatthew Dillon 			    options.subsystem_name[i]);
1969*ba1276acSMatthew Dillon 			channel_set_xtype(ssh, s->chanid, type);
1970*ba1276acSMatthew Dillon 			free(type);
19712c9c1408SMatthew Dillon 			success = do_exec(ssh, s, cmd) == 0;
197218de8d7fSPeter Avalos 			break;
197318de8d7fSPeter Avalos 		}
197418de8d7fSPeter Avalos 	}
197518de8d7fSPeter Avalos 
197618de8d7fSPeter Avalos 	if (!success)
197736e94dc5SPeter Avalos 		logit("subsystem request for %.100s by user %s failed, "
197836e94dc5SPeter Avalos 		    "subsystem not found", s->subsys, s->pw->pw_name);
197918de8d7fSPeter Avalos 
198018de8d7fSPeter Avalos 	return success;
198118de8d7fSPeter Avalos }
198218de8d7fSPeter Avalos 
198318de8d7fSPeter Avalos static int
session_x11_req(struct ssh * ssh,Session * s)19842c9c1408SMatthew Dillon session_x11_req(struct ssh *ssh, Session *s)
198518de8d7fSPeter Avalos {
1986664f4763Szrj 	int r, success;
1987664f4763Szrj 	u_char single_connection = 0;
198818de8d7fSPeter Avalos 
198918de8d7fSPeter Avalos 	if (s->auth_proto != NULL || s->auth_data != NULL) {
199018de8d7fSPeter Avalos 		error("session_x11_req: session %d: "
199118de8d7fSPeter Avalos 		    "x11 forwarding already active", s->self);
199218de8d7fSPeter Avalos 		return 0;
199318de8d7fSPeter Avalos 	}
1994664f4763Szrj 	if ((r = sshpkt_get_u8(ssh, &single_connection)) != 0 ||
1995664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &s->auth_proto, NULL)) != 0 ||
1996664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &s->auth_data, NULL)) != 0 ||
1997664f4763Szrj 	    (r = sshpkt_get_u32(ssh, &s->screen)) != 0 ||
1998664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
1999664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2000664f4763Szrj 
2001664f4763Szrj 	s->single_connection = single_connection;
200218de8d7fSPeter Avalos 
2003e9778795SPeter Avalos 	if (xauth_valid_string(s->auth_proto) &&
2004e9778795SPeter Avalos 	    xauth_valid_string(s->auth_data))
20052c9c1408SMatthew Dillon 		success = session_setup_x11fwd(ssh, s);
2006e9778795SPeter Avalos 	else {
2007e9778795SPeter Avalos 		success = 0;
2008e9778795SPeter Avalos 		error("Invalid X11 forwarding data");
2009e9778795SPeter Avalos 	}
201018de8d7fSPeter Avalos 	if (!success) {
201136e94dc5SPeter Avalos 		free(s->auth_proto);
201236e94dc5SPeter Avalos 		free(s->auth_data);
201318de8d7fSPeter Avalos 		s->auth_proto = NULL;
201418de8d7fSPeter Avalos 		s->auth_data = NULL;
201518de8d7fSPeter Avalos 	}
201618de8d7fSPeter Avalos 	return success;
201718de8d7fSPeter Avalos }
201818de8d7fSPeter Avalos 
201918de8d7fSPeter Avalos static int
session_shell_req(struct ssh * ssh,Session * s)20202c9c1408SMatthew Dillon session_shell_req(struct ssh *ssh, Session *s)
202118de8d7fSPeter Avalos {
2022664f4763Szrj 	int r;
2023664f4763Szrj 
2024664f4763Szrj 	if ((r = sshpkt_get_end(ssh)) != 0)
2025664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2026*ba1276acSMatthew Dillon 
2027*ba1276acSMatthew Dillon 	channel_set_xtype(ssh, s->chanid, "session:shell");
2028*ba1276acSMatthew Dillon 
20292c9c1408SMatthew Dillon 	return do_exec(ssh, s, NULL) == 0;
203018de8d7fSPeter Avalos }
203118de8d7fSPeter Avalos 
203218de8d7fSPeter Avalos static int
session_exec_req(struct ssh * ssh,Session * s)20332c9c1408SMatthew Dillon session_exec_req(struct ssh *ssh, Session *s)
203418de8d7fSPeter Avalos {
2035664f4763Szrj 	u_int success;
2036664f4763Szrj 	int r;
2037664f4763Szrj 	char *command = NULL;
203818de8d7fSPeter Avalos 
2039664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &command, NULL)) != 0 ||
2040664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
2041664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2042664f4763Szrj 
2043*ba1276acSMatthew Dillon 	channel_set_xtype(ssh, s->chanid, "session:command");
2044*ba1276acSMatthew Dillon 
20452c9c1408SMatthew Dillon 	success = do_exec(ssh, s, command) == 0;
204636e94dc5SPeter Avalos 	free(command);
204718de8d7fSPeter Avalos 	return success;
204818de8d7fSPeter Avalos }
204918de8d7fSPeter Avalos 
205018de8d7fSPeter Avalos static int
session_break_req(struct ssh * ssh,Session * s)20512c9c1408SMatthew Dillon session_break_req(struct ssh *ssh, Session *s)
205218de8d7fSPeter Avalos {
2053664f4763Szrj 	int r;
205418de8d7fSPeter Avalos 
2055664f4763Szrj 	if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* ignore */
2056664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
2057664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
205818de8d7fSPeter Avalos 
20590cbfa66cSDaniel Fojt 	if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) == -1)
206018de8d7fSPeter Avalos 		return 0;
206118de8d7fSPeter Avalos 	return 1;
206218de8d7fSPeter Avalos }
206318de8d7fSPeter Avalos 
206418de8d7fSPeter Avalos static int
session_env_req(struct ssh * ssh,Session * s)20652c9c1408SMatthew Dillon session_env_req(struct ssh *ssh, Session *s)
206618de8d7fSPeter Avalos {
206718de8d7fSPeter Avalos 	char *name, *val;
2068664f4763Szrj 	u_int i;
2069664f4763Szrj 	int r;
207018de8d7fSPeter Avalos 
2071664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 ||
2072664f4763Szrj 	    (r = sshpkt_get_cstring(ssh, &val, NULL)) != 0 ||
2073664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0)
2074664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
207518de8d7fSPeter Avalos 
207618de8d7fSPeter Avalos 	/* Don't set too many environment variables */
207718de8d7fSPeter Avalos 	if (s->num_env > 128) {
207818de8d7fSPeter Avalos 		debug2("Ignoring env request %s: too many env vars", name);
207918de8d7fSPeter Avalos 		goto fail;
208018de8d7fSPeter Avalos 	}
208118de8d7fSPeter Avalos 
208218de8d7fSPeter Avalos 	for (i = 0; i < options.num_accept_env; i++) {
208318de8d7fSPeter Avalos 		if (match_pattern(name, options.accept_env[i])) {
208418de8d7fSPeter Avalos 			debug2("Setting env %d: %s=%s", s->num_env, name, val);
20852c9c1408SMatthew Dillon 			s->env = xrecallocarray(s->env, s->num_env,
20862c9c1408SMatthew Dillon 			    s->num_env + 1, sizeof(*s->env));
208718de8d7fSPeter Avalos 			s->env[s->num_env].name = name;
208818de8d7fSPeter Avalos 			s->env[s->num_env].val = val;
208918de8d7fSPeter Avalos 			s->num_env++;
209018de8d7fSPeter Avalos 			return (1);
209118de8d7fSPeter Avalos 		}
209218de8d7fSPeter Avalos 	}
209318de8d7fSPeter Avalos 	debug2("Ignoring env request %s: disallowed name", name);
209418de8d7fSPeter Avalos 
209518de8d7fSPeter Avalos  fail:
209636e94dc5SPeter Avalos 	free(name);
209736e94dc5SPeter Avalos 	free(val);
209818de8d7fSPeter Avalos 	return (0);
209918de8d7fSPeter Avalos }
210018de8d7fSPeter Avalos 
2101664f4763Szrj /*
2102664f4763Szrj  * Conversion of signals from ssh channel request names.
2103664f4763Szrj  * Subset of signals from RFC 4254 section 6.10C, with SIGINFO as
2104664f4763Szrj  * local extension.
2105664f4763Szrj  */
2106664f4763Szrj static int
name2sig(char * name)2107664f4763Szrj name2sig(char *name)
2108664f4763Szrj {
2109664f4763Szrj #define SSH_SIG(x) if (strcmp(name, #x) == 0) return SIG ## x
2110664f4763Szrj 	SSH_SIG(HUP);
2111664f4763Szrj 	SSH_SIG(INT);
2112664f4763Szrj 	SSH_SIG(KILL);
2113664f4763Szrj 	SSH_SIG(QUIT);
2114664f4763Szrj 	SSH_SIG(TERM);
2115664f4763Szrj 	SSH_SIG(USR1);
2116664f4763Szrj 	SSH_SIG(USR2);
2117664f4763Szrj #undef	SSH_SIG
2118664f4763Szrj #ifdef SIGINFO
2119664f4763Szrj 	if (strcmp(name, "INFO@openssh.com") == 0)
2120664f4763Szrj 		return SIGINFO;
2121664f4763Szrj #endif
2122664f4763Szrj 	return -1;
2123664f4763Szrj }
2124664f4763Szrj 
2125664f4763Szrj static int
session_signal_req(struct ssh * ssh,Session * s)2126664f4763Szrj session_signal_req(struct ssh *ssh, Session *s)
2127664f4763Szrj {
2128664f4763Szrj 	char *signame = NULL;
2129664f4763Szrj 	int r, sig, success = 0;
2130664f4763Szrj 
2131664f4763Szrj 	if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 ||
2132664f4763Szrj 	    (r = sshpkt_get_end(ssh)) != 0) {
213350a69bb5SSascha Wildner 		error_fr(r, "parse");
2134664f4763Szrj 		goto out;
2135664f4763Szrj 	}
2136664f4763Szrj 	if ((sig = name2sig(signame)) == -1) {
213750a69bb5SSascha Wildner 		error_f("unsupported signal \"%s\"", signame);
2138664f4763Szrj 		goto out;
2139664f4763Szrj 	}
2140664f4763Szrj 	if (s->pid <= 0) {
214150a69bb5SSascha Wildner 		error_f("no pid for session %d", s->self);
2142664f4763Szrj 		goto out;
2143664f4763Szrj 	}
2144664f4763Szrj 	if (s->forced || s->is_subsystem) {
214550a69bb5SSascha Wildner 		error_f("refusing to send signal %s to %s session",
2146664f4763Szrj 		    signame, s->forced ? "forced-command" : "subsystem");
2147664f4763Szrj 		goto out;
2148664f4763Szrj 	}
2149*ba1276acSMatthew Dillon 	if (mm_is_monitor()) {
215050a69bb5SSascha Wildner 		error_f("session signalling requires privilege separation");
2151664f4763Szrj 		goto out;
2152664f4763Szrj 	}
2153664f4763Szrj 
215450a69bb5SSascha Wildner 	debug_f("signal %s, killpg(%ld, %d)", signame, (long)s->pid, sig);
2155664f4763Szrj 	temporarily_use_uid(s->pw);
2156664f4763Szrj 	r = killpg(s->pid, sig);
2157664f4763Szrj 	restore_uid();
2158664f4763Szrj 	if (r != 0) {
215950a69bb5SSascha Wildner 		error_f("killpg(%ld, %d): %s", (long)s->pid,
2160664f4763Szrj 		    sig, strerror(errno));
2161664f4763Szrj 		goto out;
2162664f4763Szrj 	}
2163664f4763Szrj 
2164664f4763Szrj 	/* success */
2165664f4763Szrj 	success = 1;
2166664f4763Szrj  out:
2167664f4763Szrj 	free(signame);
2168664f4763Szrj 	return success;
2169664f4763Szrj }
2170664f4763Szrj 
217118de8d7fSPeter Avalos static int
session_auth_agent_req(struct ssh * ssh,Session * s)21722c9c1408SMatthew Dillon session_auth_agent_req(struct ssh *ssh, Session *s)
217318de8d7fSPeter Avalos {
217418de8d7fSPeter Avalos 	static int called = 0;
2175664f4763Szrj 	int r;
2176664f4763Szrj 
2177664f4763Szrj 	if ((r = sshpkt_get_end(ssh)) != 0)
2178664f4763Szrj 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
2179664f4763Szrj 	if (!auth_opts->permit_agent_forwarding_flag ||
2180664f4763Szrj 	    !options.allow_agent_forwarding) {
218150a69bb5SSascha Wildner 		debug_f("agent forwarding disabled");
218218de8d7fSPeter Avalos 		return 0;
218318de8d7fSPeter Avalos 	}
218418de8d7fSPeter Avalos 	if (called) {
218518de8d7fSPeter Avalos 		return 0;
218618de8d7fSPeter Avalos 	} else {
218718de8d7fSPeter Avalos 		called = 1;
21882c9c1408SMatthew Dillon 		return auth_input_request_forwarding(ssh, s->pw);
218918de8d7fSPeter Avalos 	}
219018de8d7fSPeter Avalos }
219118de8d7fSPeter Avalos 
219218de8d7fSPeter Avalos int
session_input_channel_req(struct ssh * ssh,Channel * c,const char * rtype)21932c9c1408SMatthew Dillon session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype)
219418de8d7fSPeter Avalos {
219518de8d7fSPeter Avalos 	int success = 0;
219618de8d7fSPeter Avalos 	Session *s;
219718de8d7fSPeter Avalos 
219818de8d7fSPeter Avalos 	if ((s = session_by_channel(c->self)) == NULL) {
219950a69bb5SSascha Wildner 		logit_f("no session %d req %.100s", c->self, rtype);
220018de8d7fSPeter Avalos 		return 0;
220118de8d7fSPeter Avalos 	}
220250a69bb5SSascha Wildner 	debug_f("session %d req %s", s->self, rtype);
220318de8d7fSPeter Avalos 
220418de8d7fSPeter Avalos 	/*
220518de8d7fSPeter Avalos 	 * a session is in LARVAL state until a shell, a command
220618de8d7fSPeter Avalos 	 * or a subsystem is executed
220718de8d7fSPeter Avalos 	 */
220818de8d7fSPeter Avalos 	if (c->type == SSH_CHANNEL_LARVAL) {
220918de8d7fSPeter Avalos 		if (strcmp(rtype, "shell") == 0) {
22102c9c1408SMatthew Dillon 			success = session_shell_req(ssh, s);
221118de8d7fSPeter Avalos 		} else if (strcmp(rtype, "exec") == 0) {
22122c9c1408SMatthew Dillon 			success = session_exec_req(ssh, s);
221318de8d7fSPeter Avalos 		} else if (strcmp(rtype, "pty-req") == 0) {
22142c9c1408SMatthew Dillon 			success = session_pty_req(ssh, s);
221518de8d7fSPeter Avalos 		} else if (strcmp(rtype, "x11-req") == 0) {
22162c9c1408SMatthew Dillon 			success = session_x11_req(ssh, s);
221718de8d7fSPeter Avalos 		} else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
22182c9c1408SMatthew Dillon 			success = session_auth_agent_req(ssh, s);
221918de8d7fSPeter Avalos 		} else if (strcmp(rtype, "subsystem") == 0) {
22202c9c1408SMatthew Dillon 			success = session_subsystem_req(ssh, s);
222118de8d7fSPeter Avalos 		} else if (strcmp(rtype, "env") == 0) {
22222c9c1408SMatthew Dillon 			success = session_env_req(ssh, s);
222318de8d7fSPeter Avalos 		}
222418de8d7fSPeter Avalos 	}
222518de8d7fSPeter Avalos 	if (strcmp(rtype, "window-change") == 0) {
22262c9c1408SMatthew Dillon 		success = session_window_change_req(ssh, s);
222718de8d7fSPeter Avalos 	} else if (strcmp(rtype, "break") == 0) {
22282c9c1408SMatthew Dillon 		success = session_break_req(ssh, s);
2229664f4763Szrj 	} else if (strcmp(rtype, "signal") == 0) {
2230664f4763Szrj 		success = session_signal_req(ssh, s);
223118de8d7fSPeter Avalos 	}
223218de8d7fSPeter Avalos 
223318de8d7fSPeter Avalos 	return success;
223418de8d7fSPeter Avalos }
223518de8d7fSPeter Avalos 
223618de8d7fSPeter Avalos void
session_set_fds(struct ssh * ssh,Session * s,int fdin,int fdout,int fderr,int ignore_fderr,int is_tty)22372c9c1408SMatthew Dillon session_set_fds(struct ssh *ssh, Session *s,
22382c9c1408SMatthew Dillon     int fdin, int fdout, int fderr, int ignore_fderr, int is_tty)
223918de8d7fSPeter Avalos {
224018de8d7fSPeter Avalos 	/*
224118de8d7fSPeter Avalos 	 * now that have a child and a pipe to the child,
224218de8d7fSPeter Avalos 	 * we can activate our channel and register the fd's
224318de8d7fSPeter Avalos 	 */
224418de8d7fSPeter Avalos 	if (s->chanid == -1)
224518de8d7fSPeter Avalos 		fatal("no channel for session %d", s->self);
22462c9c1408SMatthew Dillon 	channel_set_fds(ssh, s->chanid,
224718de8d7fSPeter Avalos 	    fdout, fdin, fderr,
2248856ea928SPeter Avalos 	    ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
224918de8d7fSPeter Avalos 	    1, is_tty, CHAN_SES_WINDOW_DEFAULT);
225018de8d7fSPeter Avalos }
225118de8d7fSPeter Avalos 
225218de8d7fSPeter Avalos /*
225318de8d7fSPeter Avalos  * Function to perform pty cleanup. Also called if we get aborted abnormally
225418de8d7fSPeter Avalos  * (e.g., due to a dropped connection).
225518de8d7fSPeter Avalos  */
225618de8d7fSPeter Avalos void
session_pty_cleanup2(Session * s)225718de8d7fSPeter Avalos session_pty_cleanup2(Session *s)
225818de8d7fSPeter Avalos {
225918de8d7fSPeter Avalos 	if (s == NULL) {
226050a69bb5SSascha Wildner 		error_f("no session");
226118de8d7fSPeter Avalos 		return;
226218de8d7fSPeter Avalos 	}
226318de8d7fSPeter Avalos 	if (s->ttyfd == -1)
226418de8d7fSPeter Avalos 		return;
226518de8d7fSPeter Avalos 
226650a69bb5SSascha Wildner 	debug_f("session %d release %s", s->self, s->tty);
226718de8d7fSPeter Avalos 
226818de8d7fSPeter Avalos 	/* Record that the user has logged out. */
226918de8d7fSPeter Avalos 	if (s->pid != 0)
227018de8d7fSPeter Avalos 		record_logout(s->pid, s->tty, s->pw->pw_name);
227118de8d7fSPeter Avalos 
227218de8d7fSPeter Avalos 	/* Release the pseudo-tty. */
227318de8d7fSPeter Avalos 	if (getuid() == 0)
227418de8d7fSPeter Avalos 		pty_release(s->tty);
227518de8d7fSPeter Avalos 
227618de8d7fSPeter Avalos 	/*
227718de8d7fSPeter Avalos 	 * Close the server side of the socket pairs.  We must do this after
227818de8d7fSPeter Avalos 	 * the pty cleanup, so that another process doesn't get this pty
227918de8d7fSPeter Avalos 	 * while we're still cleaning up.
228018de8d7fSPeter Avalos 	 */
22810cbfa66cSDaniel Fojt 	if (s->ptymaster != -1 && close(s->ptymaster) == -1)
228218de8d7fSPeter Avalos 		error("close(s->ptymaster/%d): %s",
228318de8d7fSPeter Avalos 		    s->ptymaster, strerror(errno));
228418de8d7fSPeter Avalos 
228518de8d7fSPeter Avalos 	/* unlink pty from session */
228618de8d7fSPeter Avalos 	s->ttyfd = -1;
228718de8d7fSPeter Avalos }
228818de8d7fSPeter Avalos 
228918de8d7fSPeter Avalos void
session_pty_cleanup(Session * s)229018de8d7fSPeter Avalos session_pty_cleanup(Session *s)
229118de8d7fSPeter Avalos {
2292*ba1276acSMatthew Dillon 	mm_session_pty_cleanup2(s);
229318de8d7fSPeter Avalos }
229418de8d7fSPeter Avalos 
229518de8d7fSPeter Avalos static char *
sig2name(int sig)229618de8d7fSPeter Avalos sig2name(int sig)
229718de8d7fSPeter Avalos {
229818de8d7fSPeter Avalos #define SSH_SIG(x) if (sig == SIG ## x) return #x
229918de8d7fSPeter Avalos 	SSH_SIG(ABRT);
230018de8d7fSPeter Avalos 	SSH_SIG(ALRM);
230118de8d7fSPeter Avalos 	SSH_SIG(FPE);
230218de8d7fSPeter Avalos 	SSH_SIG(HUP);
230318de8d7fSPeter Avalos 	SSH_SIG(ILL);
230418de8d7fSPeter Avalos 	SSH_SIG(INT);
230518de8d7fSPeter Avalos 	SSH_SIG(KILL);
230618de8d7fSPeter Avalos 	SSH_SIG(PIPE);
230718de8d7fSPeter Avalos 	SSH_SIG(QUIT);
230818de8d7fSPeter Avalos 	SSH_SIG(SEGV);
230918de8d7fSPeter Avalos 	SSH_SIG(TERM);
231018de8d7fSPeter Avalos 	SSH_SIG(USR1);
231118de8d7fSPeter Avalos 	SSH_SIG(USR2);
231218de8d7fSPeter Avalos #undef	SSH_SIG
231318de8d7fSPeter Avalos 	return "SIG@openssh.com";
231418de8d7fSPeter Avalos }
231518de8d7fSPeter Avalos 
231618de8d7fSPeter Avalos static void
session_close_x11(struct ssh * ssh,int id)23172c9c1408SMatthew Dillon session_close_x11(struct ssh *ssh, int id)
231818de8d7fSPeter Avalos {
231918de8d7fSPeter Avalos 	Channel *c;
232018de8d7fSPeter Avalos 
23212c9c1408SMatthew Dillon 	if ((c = channel_by_id(ssh, id)) == NULL) {
232250a69bb5SSascha Wildner 		debug_f("x11 channel %d missing", id);
232318de8d7fSPeter Avalos 	} else {
232418de8d7fSPeter Avalos 		/* Detach X11 listener */
232550a69bb5SSascha Wildner 		debug_f("detach x11 channel %d", id);
23262c9c1408SMatthew Dillon 		channel_cancel_cleanup(ssh, id);
232718de8d7fSPeter Avalos 		if (c->ostate != CHAN_OUTPUT_CLOSED)
23282c9c1408SMatthew Dillon 			chan_mark_dead(ssh, c);
232918de8d7fSPeter Avalos 	}
233018de8d7fSPeter Avalos }
233118de8d7fSPeter Avalos 
233218de8d7fSPeter Avalos static void
session_close_single_x11(struct ssh * ssh,int id,int force,void * arg)2333*ba1276acSMatthew Dillon session_close_single_x11(struct ssh *ssh, int id, int force, void *arg)
233418de8d7fSPeter Avalos {
233518de8d7fSPeter Avalos 	Session *s;
233618de8d7fSPeter Avalos 	u_int i;
233718de8d7fSPeter Avalos 
233850a69bb5SSascha Wildner 	debug3_f("channel %d", id);
23392c9c1408SMatthew Dillon 	channel_cancel_cleanup(ssh, id);
234018de8d7fSPeter Avalos 	if ((s = session_by_x11_channel(id)) == NULL)
234150a69bb5SSascha Wildner 		fatal_f("no x11 channel %d", id);
234218de8d7fSPeter Avalos 	for (i = 0; s->x11_chanids[i] != -1; i++) {
234350a69bb5SSascha Wildner 		debug_f("session %d: closing channel %d",
234450a69bb5SSascha Wildner 		    s->self, s->x11_chanids[i]);
234518de8d7fSPeter Avalos 		/*
234618de8d7fSPeter Avalos 		 * The channel "id" is already closing, but make sure we
234718de8d7fSPeter Avalos 		 * close all of its siblings.
234818de8d7fSPeter Avalos 		 */
234918de8d7fSPeter Avalos 		if (s->x11_chanids[i] != id)
23502c9c1408SMatthew Dillon 			session_close_x11(ssh, s->x11_chanids[i]);
235118de8d7fSPeter Avalos 	}
235236e94dc5SPeter Avalos 	free(s->x11_chanids);
235318de8d7fSPeter Avalos 	s->x11_chanids = NULL;
235436e94dc5SPeter Avalos 	free(s->display);
235518de8d7fSPeter Avalos 	s->display = NULL;
235636e94dc5SPeter Avalos 	free(s->auth_proto);
235718de8d7fSPeter Avalos 	s->auth_proto = NULL;
235836e94dc5SPeter Avalos 	free(s->auth_data);
235918de8d7fSPeter Avalos 	s->auth_data = NULL;
236036e94dc5SPeter Avalos 	free(s->auth_display);
236118de8d7fSPeter Avalos 	s->auth_display = NULL;
236218de8d7fSPeter Avalos }
236318de8d7fSPeter Avalos 
236418de8d7fSPeter Avalos static void
session_exit_message(struct ssh * ssh,Session * s,int status)23652c9c1408SMatthew Dillon session_exit_message(struct ssh *ssh, Session *s, int status)
236618de8d7fSPeter Avalos {
236718de8d7fSPeter Avalos 	Channel *c;
2368664f4763Szrj 	int r;
2369*ba1276acSMatthew Dillon 	char *note = NULL;
237018de8d7fSPeter Avalos 
23712c9c1408SMatthew Dillon 	if ((c = channel_lookup(ssh, s->chanid)) == NULL)
237250a69bb5SSascha Wildner 		fatal_f("session %d: no channel %d", s->self, s->chanid);
237318de8d7fSPeter Avalos 
237418de8d7fSPeter Avalos 	if (WIFEXITED(status)) {
23752c9c1408SMatthew Dillon 		channel_request_start(ssh, s->chanid, "exit-status", 0);
2376664f4763Szrj 		if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 ||
2377664f4763Szrj 		    (r = sshpkt_send(ssh)) != 0)
2378664f4763Szrj 			sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
2379*ba1276acSMatthew Dillon 		xasprintf(&note, "exit %d", WEXITSTATUS(status));
238018de8d7fSPeter Avalos 	} else if (WIFSIGNALED(status)) {
23812c9c1408SMatthew Dillon 		channel_request_start(ssh, s->chanid, "exit-signal", 0);
2382664f4763Szrj #ifndef WCOREDUMP
2383664f4763Szrj # define WCOREDUMP(x) (0)
2384664f4763Szrj #endif
2385664f4763Szrj 		if ((r = sshpkt_put_cstring(ssh, sig2name(WTERMSIG(status)))) != 0 ||
2386664f4763Szrj 		    (r = sshpkt_put_u8(ssh, WCOREDUMP(status)? 1 : 0)) != 0 ||
2387664f4763Szrj 		    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
2388664f4763Szrj 		    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
2389664f4763Szrj 		    (r = sshpkt_send(ssh)) != 0)
2390664f4763Szrj 			sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
2391*ba1276acSMatthew Dillon 		xasprintf(&note, "signal %d%s", WTERMSIG(status),
2392*ba1276acSMatthew Dillon 		    WCOREDUMP(status) ? " core dumped" : "");
239318de8d7fSPeter Avalos 	} else {
239418de8d7fSPeter Avalos 		/* Some weird exit cause.  Just exit. */
2395*ba1276acSMatthew Dillon 		ssh_packet_disconnect(ssh, "wait returned status %04x.",
2396*ba1276acSMatthew Dillon 		    status);
239718de8d7fSPeter Avalos 	}
239818de8d7fSPeter Avalos 
2399*ba1276acSMatthew Dillon 	debug_f("session %d channel %d pid %ld %s", s->self, s->chanid,
2400*ba1276acSMatthew Dillon 	    (long)s->pid, note == NULL ? "UNKNOWN" : note);
2401*ba1276acSMatthew Dillon 	free(note);
2402*ba1276acSMatthew Dillon 
240318de8d7fSPeter Avalos 	/* disconnect channel */
240450a69bb5SSascha Wildner 	debug_f("release channel %d", s->chanid);
240518de8d7fSPeter Avalos 
240618de8d7fSPeter Avalos 	/*
240718de8d7fSPeter Avalos 	 * Adjust cleanup callback attachment to send close messages when
240818de8d7fSPeter Avalos 	 * the channel gets EOF. The session will be then be closed
24090cbfa66cSDaniel Fojt 	 * by session_close_by_channel when the child sessions close their fds.
241018de8d7fSPeter Avalos 	 */
24112c9c1408SMatthew Dillon 	channel_register_cleanup(ssh, c->self, session_close_by_channel, 1);
241218de8d7fSPeter Avalos 
241318de8d7fSPeter Avalos 	/*
241418de8d7fSPeter Avalos 	 * emulate a write failure with 'chan_write_failed', nobody will be
241518de8d7fSPeter Avalos 	 * interested in data we write.
241618de8d7fSPeter Avalos 	 * Note that we must not call 'chan_read_failed', since there could
241718de8d7fSPeter Avalos 	 * be some more data waiting in the pipe.
241818de8d7fSPeter Avalos 	 */
241918de8d7fSPeter Avalos 	if (c->ostate != CHAN_OUTPUT_CLOSED)
24202c9c1408SMatthew Dillon 		chan_write_failed(ssh, c);
242118de8d7fSPeter Avalos }
242218de8d7fSPeter Avalos 
242318de8d7fSPeter Avalos void
session_close(struct ssh * ssh,Session * s)24242c9c1408SMatthew Dillon session_close(struct ssh *ssh, Session *s)
242518de8d7fSPeter Avalos {
242618de8d7fSPeter Avalos 	u_int i;
242718de8d7fSPeter Avalos 
2428e9778795SPeter Avalos 	verbose("Close session: user %s from %.200s port %d id %d",
2429e9778795SPeter Avalos 	    s->pw->pw_name,
2430e9778795SPeter Avalos 	    ssh_remote_ipaddr(ssh),
2431e9778795SPeter Avalos 	    ssh_remote_port(ssh),
2432e9778795SPeter Avalos 	    s->self);
2433e9778795SPeter Avalos 
243418de8d7fSPeter Avalos 	if (s->ttyfd != -1)
243518de8d7fSPeter Avalos 		session_pty_cleanup(s);
243636e94dc5SPeter Avalos 	free(s->term);
243736e94dc5SPeter Avalos 	free(s->display);
243836e94dc5SPeter Avalos 	free(s->x11_chanids);
243936e94dc5SPeter Avalos 	free(s->auth_display);
244036e94dc5SPeter Avalos 	free(s->auth_data);
244136e94dc5SPeter Avalos 	free(s->auth_proto);
244236e94dc5SPeter Avalos 	free(s->subsys);
244318de8d7fSPeter Avalos 	if (s->env != NULL) {
244418de8d7fSPeter Avalos 		for (i = 0; i < s->num_env; i++) {
244536e94dc5SPeter Avalos 			free(s->env[i].name);
244636e94dc5SPeter Avalos 			free(s->env[i].val);
244718de8d7fSPeter Avalos 		}
244836e94dc5SPeter Avalos 		free(s->env);
244918de8d7fSPeter Avalos 	}
245018de8d7fSPeter Avalos 	session_proctitle(s);
245118de8d7fSPeter Avalos 	session_unused(s->self);
245218de8d7fSPeter Avalos }
245318de8d7fSPeter Avalos 
245418de8d7fSPeter Avalos void
session_close_by_pid(struct ssh * ssh,pid_t pid,int status)24552c9c1408SMatthew Dillon session_close_by_pid(struct ssh *ssh, pid_t pid, int status)
245618de8d7fSPeter Avalos {
245718de8d7fSPeter Avalos 	Session *s = session_by_pid(pid);
245818de8d7fSPeter Avalos 	if (s == NULL) {
245950a69bb5SSascha Wildner 		debug_f("no session for pid %ld", (long)pid);
246018de8d7fSPeter Avalos 		return;
246118de8d7fSPeter Avalos 	}
246218de8d7fSPeter Avalos 	if (s->chanid != -1)
24632c9c1408SMatthew Dillon 		session_exit_message(ssh, s, status);
246418de8d7fSPeter Avalos 	if (s->ttyfd != -1)
246518de8d7fSPeter Avalos 		session_pty_cleanup(s);
246618de8d7fSPeter Avalos 	s->pid = 0;
246718de8d7fSPeter Avalos }
246818de8d7fSPeter Avalos 
246918de8d7fSPeter Avalos /*
247018de8d7fSPeter Avalos  * this is called when a channel dies before
247118de8d7fSPeter Avalos  * the session 'child' itself dies
247218de8d7fSPeter Avalos  */
247318de8d7fSPeter Avalos void
session_close_by_channel(struct ssh * ssh,int id,int force,void * arg)2474*ba1276acSMatthew Dillon session_close_by_channel(struct ssh *ssh, int id, int force, void *arg)
247518de8d7fSPeter Avalos {
247618de8d7fSPeter Avalos 	Session *s = session_by_channel(id);
247718de8d7fSPeter Avalos 	u_int i;
247818de8d7fSPeter Avalos 
247918de8d7fSPeter Avalos 	if (s == NULL) {
248050a69bb5SSascha Wildner 		debug_f("no session for id %d", id);
248118de8d7fSPeter Avalos 		return;
248218de8d7fSPeter Avalos 	}
248350a69bb5SSascha Wildner 	debug_f("channel %d child %ld", id, (long)s->pid);
248418de8d7fSPeter Avalos 	if (s->pid != 0) {
248550a69bb5SSascha Wildner 		debug_f("channel %d: has child, ttyfd %d", id, s->ttyfd);
248618de8d7fSPeter Avalos 		/*
2487*ba1276acSMatthew Dillon 		 * delay detach of session (unless this is a forced close),
2488*ba1276acSMatthew Dillon 		 * but release pty, since the fd's to the child are already
2489*ba1276acSMatthew Dillon 		 * closed
249018de8d7fSPeter Avalos 		 */
249118de8d7fSPeter Avalos 		if (s->ttyfd != -1)
249218de8d7fSPeter Avalos 			session_pty_cleanup(s);
2493*ba1276acSMatthew Dillon 		if (!force)
249418de8d7fSPeter Avalos 			return;
249518de8d7fSPeter Avalos 	}
249618de8d7fSPeter Avalos 	/* detach by removing callback */
24972c9c1408SMatthew Dillon 	channel_cancel_cleanup(ssh, s->chanid);
249818de8d7fSPeter Avalos 
249918de8d7fSPeter Avalos 	/* Close any X11 listeners associated with this session */
250018de8d7fSPeter Avalos 	if (s->x11_chanids != NULL) {
250118de8d7fSPeter Avalos 		for (i = 0; s->x11_chanids[i] != -1; i++) {
25022c9c1408SMatthew Dillon 			session_close_x11(ssh, s->x11_chanids[i]);
250318de8d7fSPeter Avalos 			s->x11_chanids[i] = -1;
250418de8d7fSPeter Avalos 		}
250518de8d7fSPeter Avalos 	}
250618de8d7fSPeter Avalos 
250718de8d7fSPeter Avalos 	s->chanid = -1;
25082c9c1408SMatthew Dillon 	session_close(ssh, s);
250918de8d7fSPeter Avalos }
251018de8d7fSPeter Avalos 
251118de8d7fSPeter Avalos void
session_destroy_all(struct ssh * ssh,void (* closefunc)(Session *))25122c9c1408SMatthew Dillon session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *))
251318de8d7fSPeter Avalos {
251418de8d7fSPeter Avalos 	int i;
251518de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
251618de8d7fSPeter Avalos 		Session *s = &sessions[i];
251718de8d7fSPeter Avalos 		if (s->used) {
251818de8d7fSPeter Avalos 			if (closefunc != NULL)
251918de8d7fSPeter Avalos 				closefunc(s);
252018de8d7fSPeter Avalos 			else
25212c9c1408SMatthew Dillon 				session_close(ssh, s);
252218de8d7fSPeter Avalos 		}
252318de8d7fSPeter Avalos 	}
252418de8d7fSPeter Avalos }
252518de8d7fSPeter Avalos 
252618de8d7fSPeter Avalos static char *
session_tty_list(void)252718de8d7fSPeter Avalos session_tty_list(void)
252818de8d7fSPeter Avalos {
252918de8d7fSPeter Avalos 	static char buf[1024];
253018de8d7fSPeter Avalos 	int i;
253118de8d7fSPeter Avalos 	char *cp;
253218de8d7fSPeter Avalos 
253318de8d7fSPeter Avalos 	buf[0] = '\0';
253418de8d7fSPeter Avalos 	for (i = 0; i < sessions_nalloc; i++) {
253518de8d7fSPeter Avalos 		Session *s = &sessions[i];
253618de8d7fSPeter Avalos 		if (s->used && s->ttyfd != -1) {
253718de8d7fSPeter Avalos 
253818de8d7fSPeter Avalos 			if (strncmp(s->tty, "/dev/", 5) != 0) {
253918de8d7fSPeter Avalos 				cp = strrchr(s->tty, '/');
254018de8d7fSPeter Avalos 				cp = (cp == NULL) ? s->tty : cp + 1;
254118de8d7fSPeter Avalos 			} else
254218de8d7fSPeter Avalos 				cp = s->tty + 5;
254318de8d7fSPeter Avalos 
254418de8d7fSPeter Avalos 			if (buf[0] != '\0')
254518de8d7fSPeter Avalos 				strlcat(buf, ",", sizeof buf);
254618de8d7fSPeter Avalos 			strlcat(buf, cp, sizeof buf);
254718de8d7fSPeter Avalos 		}
254818de8d7fSPeter Avalos 	}
254918de8d7fSPeter Avalos 	if (buf[0] == '\0')
255018de8d7fSPeter Avalos 		strlcpy(buf, "notty", sizeof buf);
255118de8d7fSPeter Avalos 	return buf;
255218de8d7fSPeter Avalos }
255318de8d7fSPeter Avalos 
255418de8d7fSPeter Avalos void
session_proctitle(Session * s)255518de8d7fSPeter Avalos session_proctitle(Session *s)
255618de8d7fSPeter Avalos {
255718de8d7fSPeter Avalos 	if (s->pw == NULL)
255818de8d7fSPeter Avalos 		error("no user for session %d", s->self);
255918de8d7fSPeter Avalos 	else
256018de8d7fSPeter Avalos 		setproctitle("%s@%s", s->pw->pw_name, session_tty_list());
256118de8d7fSPeter Avalos }
256218de8d7fSPeter Avalos 
256318de8d7fSPeter Avalos int
session_setup_x11fwd(struct ssh * ssh,Session * s)25642c9c1408SMatthew Dillon session_setup_x11fwd(struct ssh *ssh, Session *s)
256518de8d7fSPeter Avalos {
256618de8d7fSPeter Avalos 	struct stat st;
256718de8d7fSPeter Avalos 	char display[512], auth_display[512];
256836e94dc5SPeter Avalos 	char hostname[NI_MAXHOST];
256918de8d7fSPeter Avalos 	u_int i;
257018de8d7fSPeter Avalos 
2571664f4763Szrj 	if (!auth_opts->permit_x11_forwarding_flag) {
2572664f4763Szrj 		ssh_packet_send_debug(ssh, "X11 forwarding disabled by key options.");
257318de8d7fSPeter Avalos 		return 0;
257418de8d7fSPeter Avalos 	}
257518de8d7fSPeter Avalos 	if (!options.x11_forwarding) {
257618de8d7fSPeter Avalos 		debug("X11 forwarding disabled in server configuration file.");
257718de8d7fSPeter Avalos 		return 0;
257818de8d7fSPeter Avalos 	}
2579e9778795SPeter Avalos 	if (options.xauth_location == NULL ||
258018de8d7fSPeter Avalos 	    (stat(options.xauth_location, &st) == -1)) {
2581664f4763Szrj 		ssh_packet_send_debug(ssh, "No xauth program; cannot forward X11.");
258218de8d7fSPeter Avalos 		return 0;
258318de8d7fSPeter Avalos 	}
258418de8d7fSPeter Avalos 	if (s->display != NULL) {
258518de8d7fSPeter Avalos 		debug("X11 display already set.");
258618de8d7fSPeter Avalos 		return 0;
258718de8d7fSPeter Avalos 	}
25882c9c1408SMatthew Dillon 	if (x11_create_display_inet(ssh, options.x11_display_offset,
258918de8d7fSPeter Avalos 	    options.x11_use_localhost, s->single_connection,
259018de8d7fSPeter Avalos 	    &s->display_number, &s->x11_chanids) == -1) {
259118de8d7fSPeter Avalos 		debug("x11_create_display_inet failed.");
259218de8d7fSPeter Avalos 		return 0;
259318de8d7fSPeter Avalos 	}
259418de8d7fSPeter Avalos 	for (i = 0; s->x11_chanids[i] != -1; i++) {
25952c9c1408SMatthew Dillon 		channel_register_cleanup(ssh, s->x11_chanids[i],
259618de8d7fSPeter Avalos 		    session_close_single_x11, 0);
259718de8d7fSPeter Avalos 	}
259818de8d7fSPeter Avalos 
259918de8d7fSPeter Avalos 	/* Set up a suitable value for the DISPLAY variable. */
26000cbfa66cSDaniel Fojt 	if (gethostname(hostname, sizeof(hostname)) == -1)
260118de8d7fSPeter Avalos 		fatal("gethostname: %.100s", strerror(errno));
260218de8d7fSPeter Avalos 	/*
260318de8d7fSPeter Avalos 	 * auth_display must be used as the displayname when the
260418de8d7fSPeter Avalos 	 * authorization entry is added with xauth(1).  This will be
260518de8d7fSPeter Avalos 	 * different than the DISPLAY string for localhost displays.
260618de8d7fSPeter Avalos 	 */
260718de8d7fSPeter Avalos 	if (options.x11_use_localhost) {
260818de8d7fSPeter Avalos 		snprintf(display, sizeof display, "localhost:%u.%u",
260918de8d7fSPeter Avalos 		    s->display_number, s->screen);
261018de8d7fSPeter Avalos 		snprintf(auth_display, sizeof auth_display, "unix:%u.%u",
261118de8d7fSPeter Avalos 		    s->display_number, s->screen);
261218de8d7fSPeter Avalos 		s->display = xstrdup(display);
261318de8d7fSPeter Avalos 		s->auth_display = xstrdup(auth_display);
261418de8d7fSPeter Avalos 	} else {
261518de8d7fSPeter Avalos #ifdef IPADDR_IN_DISPLAY
261618de8d7fSPeter Avalos 		struct hostent *he;
261718de8d7fSPeter Avalos 		struct in_addr my_addr;
261818de8d7fSPeter Avalos 
261918de8d7fSPeter Avalos 		he = gethostbyname(hostname);
262018de8d7fSPeter Avalos 		if (he == NULL) {
262118de8d7fSPeter Avalos 			error("Can't get IP address for X11 DISPLAY.");
2622664f4763Szrj 			ssh_packet_send_debug(ssh, "Can't get IP address for X11 DISPLAY.");
262318de8d7fSPeter Avalos 			return 0;
262418de8d7fSPeter Avalos 		}
262518de8d7fSPeter Avalos 		memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr));
262618de8d7fSPeter Avalos 		snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr),
262718de8d7fSPeter Avalos 		    s->display_number, s->screen);
262818de8d7fSPeter Avalos #else
262918de8d7fSPeter Avalos 		snprintf(display, sizeof display, "%.400s:%u.%u", hostname,
263018de8d7fSPeter Avalos 		    s->display_number, s->screen);
263118de8d7fSPeter Avalos #endif
263218de8d7fSPeter Avalos 		s->display = xstrdup(display);
263318de8d7fSPeter Avalos 		s->auth_display = xstrdup(display);
263418de8d7fSPeter Avalos 	}
263518de8d7fSPeter Avalos 
263618de8d7fSPeter Avalos 	return 1;
263718de8d7fSPeter Avalos }
263818de8d7fSPeter Avalos 
263918de8d7fSPeter Avalos static void
do_authenticated2(struct ssh * ssh,Authctxt * authctxt)26402c9c1408SMatthew Dillon do_authenticated2(struct ssh *ssh, Authctxt *authctxt)
264118de8d7fSPeter Avalos {
26422c9c1408SMatthew Dillon 	server_loop2(ssh, authctxt);
264318de8d7fSPeter Avalos }
264418de8d7fSPeter Avalos 
264518de8d7fSPeter Avalos void
do_cleanup(struct ssh * ssh,Authctxt * authctxt)26462c9c1408SMatthew Dillon do_cleanup(struct ssh *ssh, Authctxt *authctxt)
264718de8d7fSPeter Avalos {
264818de8d7fSPeter Avalos 	static int called = 0;
264918de8d7fSPeter Avalos 
265018de8d7fSPeter Avalos 	debug("do_cleanup");
265118de8d7fSPeter Avalos 
265218de8d7fSPeter Avalos 	/* no cleanup if we're in the child for login shell */
265318de8d7fSPeter Avalos 	if (is_child)
265418de8d7fSPeter Avalos 		return;
265518de8d7fSPeter Avalos 
265618de8d7fSPeter Avalos 	/* avoid double cleanup */
265718de8d7fSPeter Avalos 	if (called)
265818de8d7fSPeter Avalos 		return;
265918de8d7fSPeter Avalos 	called = 1;
266018de8d7fSPeter Avalos 
266118de8d7fSPeter Avalos 	if (authctxt == NULL)
266218de8d7fSPeter Avalos 		return;
266318de8d7fSPeter Avalos 
266418de8d7fSPeter Avalos #ifdef USE_PAM
266518de8d7fSPeter Avalos 	if (options.use_pam) {
266618de8d7fSPeter Avalos 		sshpam_cleanup();
266718de8d7fSPeter Avalos 		sshpam_thread_cleanup();
266818de8d7fSPeter Avalos 	}
266918de8d7fSPeter Avalos #endif
267018de8d7fSPeter Avalos 
267118de8d7fSPeter Avalos 	if (!authctxt->authenticated)
267218de8d7fSPeter Avalos 		return;
267318de8d7fSPeter Avalos 
267418de8d7fSPeter Avalos #ifdef KRB5
267518de8d7fSPeter Avalos 	if (options.kerberos_ticket_cleanup &&
267618de8d7fSPeter Avalos 	    authctxt->krb5_ctx)
267718de8d7fSPeter Avalos 		krb5_cleanup_proc(authctxt);
267818de8d7fSPeter Avalos #endif
267918de8d7fSPeter Avalos 
268018de8d7fSPeter Avalos #ifdef GSSAPI
26812c9c1408SMatthew Dillon 	if (options.gss_cleanup_creds)
268218de8d7fSPeter Avalos 		ssh_gssapi_cleanup_creds();
268318de8d7fSPeter Avalos #endif
268418de8d7fSPeter Avalos 
268518de8d7fSPeter Avalos 	/* remove agent socket */
268618de8d7fSPeter Avalos 	auth_sock_cleanup_proc(authctxt->pw);
268718de8d7fSPeter Avalos 
26882c9c1408SMatthew Dillon 	/* remove userauth info */
26892c9c1408SMatthew Dillon 	if (auth_info_file != NULL) {
26902c9c1408SMatthew Dillon 		temporarily_use_uid(authctxt->pw);
26912c9c1408SMatthew Dillon 		unlink(auth_info_file);
26922c9c1408SMatthew Dillon 		restore_uid();
26932c9c1408SMatthew Dillon 		free(auth_info_file);
26942c9c1408SMatthew Dillon 		auth_info_file = NULL;
26952c9c1408SMatthew Dillon 	}
26962c9c1408SMatthew Dillon 
269718de8d7fSPeter Avalos 	/*
269818de8d7fSPeter Avalos 	 * Cleanup ptys/utmp only if privsep is disabled,
269918de8d7fSPeter Avalos 	 * or if running in monitor.
270018de8d7fSPeter Avalos 	 */
2701*ba1276acSMatthew Dillon 	if (mm_is_monitor())
27022c9c1408SMatthew Dillon 		session_destroy_all(ssh, session_pty_cleanup2);
270318de8d7fSPeter Avalos }
2704e9778795SPeter Avalos 
2705e9778795SPeter Avalos /* Return a name for the remote host that fits inside utmp_size */
2706e9778795SPeter Avalos 
2707e9778795SPeter Avalos const char *
session_get_remote_name_or_ip(struct ssh * ssh,u_int utmp_size,int use_dns)2708e9778795SPeter Avalos session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns)
2709e9778795SPeter Avalos {
2710e9778795SPeter Avalos 	const char *remote = "";
2711e9778795SPeter Avalos 
2712e9778795SPeter Avalos 	if (utmp_size > 0)
2713e9778795SPeter Avalos 		remote = auth_get_canonical_hostname(ssh, use_dns);
2714e9778795SPeter Avalos 	if (utmp_size == 0 || strlen(remote) > utmp_size)
2715e9778795SPeter Avalos 		remote = ssh_remote_ipaddr(ssh);
2716e9778795SPeter Avalos 	return remote;
2717e9778795SPeter Avalos }
2718e9778795SPeter Avalos 
2719