xref: /freebsd/crypto/openssh/session.c (revision 2ec88e9d1bcc5eabc01e8524c586254d8c012c3b)
1462c32cbSDag-Erling Smørgrav /* $OpenBSD: session.c,v 1.260 2012/03/15 03:10:27 guenther Exp $ */
289986192SBrooks Davis /* $FreeBSD$ */
3a04a10f8SKris Kennaway /*
4a04a10f8SKris Kennaway  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5a04a10f8SKris Kennaway  *                    All rights reserved
6c2d3a559SKris Kennaway  *
7c2d3a559SKris Kennaway  * As far as I am concerned, the code I have written for this software
8c2d3a559SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
9c2d3a559SKris Kennaway  * software must be clearly marked as such, and if the derived work is
10c2d3a559SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
11c2d3a559SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
12c2d3a559SKris Kennaway  *
13a04a10f8SKris Kennaway  * SSH2 support by Markus Friedl.
14af12a3e7SDag-Erling Smørgrav  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
15e8aafc91SKris Kennaway  *
16c2d3a559SKris Kennaway  * Redistribution and use in source and binary forms, with or without
17c2d3a559SKris Kennaway  * modification, are permitted provided that the following conditions
18c2d3a559SKris Kennaway  * are met:
19c2d3a559SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
20c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
21c2d3a559SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
22c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
23c2d3a559SKris Kennaway  *    documentation and/or other materials provided with the distribution.
24c2d3a559SKris Kennaway  *
25c2d3a559SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26c2d3a559SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27c2d3a559SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28c2d3a559SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29c2d3a559SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30c2d3a559SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31c2d3a559SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32c2d3a559SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33c2d3a559SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34c2d3a559SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35a04a10f8SKris Kennaway  */
36a04a10f8SKris Kennaway 
37a04a10f8SKris Kennaway #include "includes.h"
38333ee039SDag-Erling Smørgrav __RCSID("$FreeBSD$");
39a04a10f8SKris Kennaway 
40333ee039SDag-Erling Smørgrav #include <sys/types.h>
41333ee039SDag-Erling Smørgrav #include <sys/param.h>
42333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H
43333ee039SDag-Erling Smørgrav # include <sys/stat.h>
44333ee039SDag-Erling Smørgrav #endif
45333ee039SDag-Erling Smørgrav #include <sys/socket.h>
46333ee039SDag-Erling Smørgrav #include <sys/un.h>
47333ee039SDag-Erling Smørgrav #include <sys/wait.h>
48333ee039SDag-Erling Smørgrav 
49333ee039SDag-Erling Smørgrav #include <arpa/inet.h>
50333ee039SDag-Erling Smørgrav 
51333ee039SDag-Erling Smørgrav #include <errno.h>
52e2f6069cSDag-Erling Smørgrav #include <fcntl.h>
53333ee039SDag-Erling Smørgrav #include <grp.h>
54333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H
55333ee039SDag-Erling Smørgrav #include <paths.h>
56333ee039SDag-Erling Smørgrav #endif
57333ee039SDag-Erling Smørgrav #include <pwd.h>
58333ee039SDag-Erling Smørgrav #include <signal.h>
59333ee039SDag-Erling Smørgrav #include <stdarg.h>
60333ee039SDag-Erling Smørgrav #include <stdio.h>
61333ee039SDag-Erling Smørgrav #include <stdlib.h>
62333ee039SDag-Erling Smørgrav #include <string.h>
63333ee039SDag-Erling Smørgrav #include <unistd.h>
64333ee039SDag-Erling Smørgrav 
65d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
66333ee039SDag-Erling Smørgrav #include "xmalloc.h"
67a04a10f8SKris Kennaway #include "ssh.h"
68ca3176e7SBrian Feldman #include "ssh1.h"
69ca3176e7SBrian Feldman #include "ssh2.h"
70ca3176e7SBrian Feldman #include "sshpty.h"
71a04a10f8SKris Kennaway #include "packet.h"
72a04a10f8SKris Kennaway #include "buffer.h"
7321e764dfSDag-Erling Smørgrav #include "match.h"
74a04a10f8SKris Kennaway #include "uidswap.h"
75a04a10f8SKris Kennaway #include "compat.h"
76a04a10f8SKris Kennaway #include "channels.h"
77333ee039SDag-Erling Smørgrav #include "key.h"
78333ee039SDag-Erling Smørgrav #include "cipher.h"
79333ee039SDag-Erling Smørgrav #ifdef GSSAPI
80333ee039SDag-Erling Smørgrav #include "ssh-gss.h"
81333ee039SDag-Erling Smørgrav #endif
82333ee039SDag-Erling Smørgrav #include "hostfile.h"
83a04a10f8SKris Kennaway #include "auth.h"
84c2d3a559SKris Kennaway #include "auth-options.h"
85ca3176e7SBrian Feldman #include "pathnames.h"
86ca3176e7SBrian Feldman #include "log.h"
87ca3176e7SBrian Feldman #include "servconf.h"
88ca3176e7SBrian Feldman #include "sshlogin.h"
89ca3176e7SBrian Feldman #include "serverloop.h"
90ca3176e7SBrian Feldman #include "canohost.h"
91d4af9e69SDag-Erling Smørgrav #include "misc.h"
92ca3176e7SBrian Feldman #include "session.h"
93d4ecd108SDag-Erling Smørgrav #include "kex.h"
9480628bacSDag-Erling Smørgrav #include "monitor_wrap.h"
95d4af9e69SDag-Erling Smørgrav #include "sftp.h"
96a04a10f8SKris Kennaway 
971ec0d754SDag-Erling Smørgrav #if defined(KRB5) && defined(USE_AFS)
981ec0d754SDag-Erling Smørgrav #include <kafs.h>
991ec0d754SDag-Erling Smørgrav #endif
1001ec0d754SDag-Erling Smørgrav 
101e146993eSDag-Erling Smørgrav #ifdef WITH_SELINUX
102e146993eSDag-Erling Smørgrav #include <selinux/selinux.h>
103e146993eSDag-Erling Smørgrav #endif
104e146993eSDag-Erling Smørgrav 
105cce7d346SDag-Erling Smørgrav #define IS_INTERNAL_SFTP(c) \
106cce7d346SDag-Erling Smørgrav 	(!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \
107cce7d346SDag-Erling Smørgrav 	 (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \
108cce7d346SDag-Erling Smørgrav 	  c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \
109cce7d346SDag-Erling Smørgrav 	  c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t'))
110cce7d346SDag-Erling Smørgrav 
111a04a10f8SKris Kennaway /* func */
112a04a10f8SKris Kennaway 
113a04a10f8SKris Kennaway Session *session_new(void);
114e2f6069cSDag-Erling Smørgrav void	session_set_fds(Session *, int, int, int, int, int);
1151ec0d754SDag-Erling Smørgrav void	session_pty_cleanup(Session *);
116af12a3e7SDag-Erling Smørgrav void	session_proctitle(Session *);
117af12a3e7SDag-Erling Smørgrav int	session_setup_x11fwd(Session *);
118d4af9e69SDag-Erling Smørgrav int	do_exec_pty(Session *, const char *);
119d4af9e69SDag-Erling Smørgrav int	do_exec_no_pty(Session *, const char *);
120d4af9e69SDag-Erling Smørgrav int	do_exec(Session *, const char *);
121af12a3e7SDag-Erling Smørgrav void	do_login(Session *, const char *);
122989dd127SDag-Erling Smørgrav #ifdef LOGIN_NEEDS_UTMPX
123989dd127SDag-Erling Smørgrav static void	do_pre_login(Session *s);
124989dd127SDag-Erling Smørgrav #endif
125af12a3e7SDag-Erling Smørgrav void	do_child(Session *, const char *);
126ca3176e7SBrian Feldman void	do_motd(void);
127af12a3e7SDag-Erling Smørgrav int	check_quietlogin(Session *, const char *);
128a04a10f8SKris Kennaway 
129af12a3e7SDag-Erling Smørgrav static void do_authenticated1(Authctxt *);
130af12a3e7SDag-Erling Smørgrav static void do_authenticated2(Authctxt *);
131af12a3e7SDag-Erling Smørgrav 
132af12a3e7SDag-Erling Smørgrav static int session_pty_req(Session *);
133a04a10f8SKris Kennaway 
134a04a10f8SKris Kennaway /* import */
135a04a10f8SKris Kennaway extern ServerOptions options;
136a04a10f8SKris Kennaway extern char *__progname;
137a04a10f8SKris Kennaway extern int log_stderr;
138a04a10f8SKris Kennaway extern int debug_flag;
139ca3176e7SBrian Feldman extern u_int utmp_len;
140c2d3a559SKris Kennaway extern int startup_pipe;
141ca3176e7SBrian Feldman extern void destroy_sensitive_data(void);
142cf2b5f3bSDag-Erling Smørgrav extern Buffer loginmsg;
143a04a10f8SKris Kennaway 
144c2d3a559SKris Kennaway /* original command from peer. */
145af12a3e7SDag-Erling Smørgrav const char *original_command = NULL;
146c2d3a559SKris Kennaway 
147a04a10f8SKris Kennaway /* data */
148d4af9e69SDag-Erling Smørgrav static int sessions_first_unused = -1;
149d4af9e69SDag-Erling Smørgrav static int sessions_nalloc = 0;
150d4af9e69SDag-Erling Smørgrav static Session *sessions = NULL;
151d4af9e69SDag-Erling Smørgrav 
152d4af9e69SDag-Erling Smørgrav #define SUBSYSTEM_NONE			0
153d4af9e69SDag-Erling Smørgrav #define SUBSYSTEM_EXT			1
154d4af9e69SDag-Erling Smørgrav #define SUBSYSTEM_INT_SFTP		2
155b15c8340SDag-Erling Smørgrav #define SUBSYSTEM_INT_SFTP_ERROR	3
156a04a10f8SKris Kennaway 
157c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
15880628bacSDag-Erling Smørgrav login_cap_t *lc;
159c2d3a559SKris Kennaway #endif
160a04a10f8SKris Kennaway 
1611ec0d754SDag-Erling Smørgrav static int is_child = 0;
1621ec0d754SDag-Erling Smørgrav 
16380628bacSDag-Erling Smørgrav /* Name and directory of socket for authentication agent forwarding. */
16480628bacSDag-Erling Smørgrav static char *auth_sock_name = NULL;
16580628bacSDag-Erling Smørgrav static char *auth_sock_dir = NULL;
16680628bacSDag-Erling Smørgrav 
16780628bacSDag-Erling Smørgrav /* removes the agent forwarding socket */
16880628bacSDag-Erling Smørgrav 
16980628bacSDag-Erling Smørgrav static void
1701ec0d754SDag-Erling Smørgrav auth_sock_cleanup_proc(struct passwd *pw)
17180628bacSDag-Erling Smørgrav {
17280628bacSDag-Erling Smørgrav 	if (auth_sock_name != NULL) {
17380628bacSDag-Erling Smørgrav 		temporarily_use_uid(pw);
17480628bacSDag-Erling Smørgrav 		unlink(auth_sock_name);
17580628bacSDag-Erling Smørgrav 		rmdir(auth_sock_dir);
17680628bacSDag-Erling Smørgrav 		auth_sock_name = NULL;
17780628bacSDag-Erling Smørgrav 		restore_uid();
17880628bacSDag-Erling Smørgrav 	}
17980628bacSDag-Erling Smørgrav }
18080628bacSDag-Erling Smørgrav 
18180628bacSDag-Erling Smørgrav static int
18280628bacSDag-Erling Smørgrav auth_input_request_forwarding(struct passwd * pw)
18380628bacSDag-Erling Smørgrav {
18480628bacSDag-Erling Smørgrav 	Channel *nc;
185d4af9e69SDag-Erling Smørgrav 	int sock = -1;
18680628bacSDag-Erling Smørgrav 	struct sockaddr_un sunaddr;
18780628bacSDag-Erling Smørgrav 
18880628bacSDag-Erling Smørgrav 	if (auth_sock_name != NULL) {
18980628bacSDag-Erling Smørgrav 		error("authentication forwarding requested twice.");
19080628bacSDag-Erling Smørgrav 		return 0;
19180628bacSDag-Erling Smørgrav 	}
19280628bacSDag-Erling Smørgrav 
19380628bacSDag-Erling Smørgrav 	/* Temporarily drop privileged uid for mkdir/bind. */
19480628bacSDag-Erling Smørgrav 	temporarily_use_uid(pw);
19580628bacSDag-Erling Smørgrav 
19680628bacSDag-Erling Smørgrav 	/* Allocate a buffer for the socket name, and format the name. */
197d4af9e69SDag-Erling Smørgrav 	auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
19880628bacSDag-Erling Smørgrav 
19980628bacSDag-Erling Smørgrav 	/* Create private directory for socket */
20080628bacSDag-Erling Smørgrav 	if (mkdtemp(auth_sock_dir) == NULL) {
20180628bacSDag-Erling Smørgrav 		packet_send_debug("Agent forwarding disabled: "
20280628bacSDag-Erling Smørgrav 		    "mkdtemp() failed: %.100s", strerror(errno));
20380628bacSDag-Erling Smørgrav 		restore_uid();
20480628bacSDag-Erling Smørgrav 		xfree(auth_sock_dir);
20580628bacSDag-Erling Smørgrav 		auth_sock_dir = NULL;
206d4af9e69SDag-Erling Smørgrav 		goto authsock_err;
20780628bacSDag-Erling Smørgrav 	}
208d4af9e69SDag-Erling Smørgrav 
209d4af9e69SDag-Erling Smørgrav 	xasprintf(&auth_sock_name, "%s/agent.%ld",
21080628bacSDag-Erling Smørgrav 	    auth_sock_dir, (long) getpid());
21180628bacSDag-Erling Smørgrav 
21280628bacSDag-Erling Smørgrav 	/* Create the socket. */
21380628bacSDag-Erling Smørgrav 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
214d4af9e69SDag-Erling Smørgrav 	if (sock < 0) {
215d4af9e69SDag-Erling Smørgrav 		error("socket: %.100s", strerror(errno));
216d4af9e69SDag-Erling Smørgrav 		restore_uid();
217d4af9e69SDag-Erling Smørgrav 		goto authsock_err;
218d4af9e69SDag-Erling Smørgrav 	}
21980628bacSDag-Erling Smørgrav 
22080628bacSDag-Erling Smørgrav 	/* Bind it to the name. */
22180628bacSDag-Erling Smørgrav 	memset(&sunaddr, 0, sizeof(sunaddr));
22280628bacSDag-Erling Smørgrav 	sunaddr.sun_family = AF_UNIX;
22380628bacSDag-Erling Smørgrav 	strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path));
22480628bacSDag-Erling Smørgrav 
225d4af9e69SDag-Erling Smørgrav 	if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
226d4af9e69SDag-Erling Smørgrav 		error("bind: %.100s", strerror(errno));
227d4af9e69SDag-Erling Smørgrav 		restore_uid();
228d4af9e69SDag-Erling Smørgrav 		goto authsock_err;
229d4af9e69SDag-Erling Smørgrav 	}
23080628bacSDag-Erling Smørgrav 
23180628bacSDag-Erling Smørgrav 	/* Restore the privileged uid. */
23280628bacSDag-Erling Smørgrav 	restore_uid();
23380628bacSDag-Erling Smørgrav 
23480628bacSDag-Erling Smørgrav 	/* Start listening on the socket. */
235d4af9e69SDag-Erling Smørgrav 	if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
236d4af9e69SDag-Erling Smørgrav 		error("listen: %.100s", strerror(errno));
237d4af9e69SDag-Erling Smørgrav 		goto authsock_err;
238d4af9e69SDag-Erling Smørgrav 	}
23980628bacSDag-Erling Smørgrav 
24089986192SBrooks Davis 	/*
24189986192SBrooks Davis 	 * Allocate a channel for the authentication agent socket.
24289986192SBrooks Davis 	 * Ignore HPN on that one given no improvement expected.
24389986192SBrooks Davis 	 */
24480628bacSDag-Erling Smørgrav 	nc = channel_new("auth socket",
24580628bacSDag-Erling Smørgrav 	    SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
24680628bacSDag-Erling Smørgrav 	    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
247cf2b5f3bSDag-Erling Smørgrav 	    0, "auth socket", 1);
248cce7d346SDag-Erling Smørgrav 	nc->path = xstrdup(auth_sock_name);
24980628bacSDag-Erling Smørgrav 	return 1;
250d4af9e69SDag-Erling Smørgrav 
251d4af9e69SDag-Erling Smørgrav  authsock_err:
252d4af9e69SDag-Erling Smørgrav 	if (auth_sock_name != NULL)
253d4af9e69SDag-Erling Smørgrav 		xfree(auth_sock_name);
254d4af9e69SDag-Erling Smørgrav 	if (auth_sock_dir != NULL) {
255d4af9e69SDag-Erling Smørgrav 		rmdir(auth_sock_dir);
256d4af9e69SDag-Erling Smørgrav 		xfree(auth_sock_dir);
257d4af9e69SDag-Erling Smørgrav 	}
258d4af9e69SDag-Erling Smørgrav 	if (sock != -1)
259d4af9e69SDag-Erling Smørgrav 		close(sock);
260d4af9e69SDag-Erling Smørgrav 	auth_sock_name = NULL;
261d4af9e69SDag-Erling Smørgrav 	auth_sock_dir = NULL;
262d4af9e69SDag-Erling Smørgrav 	return 0;
26380628bacSDag-Erling Smørgrav }
26480628bacSDag-Erling Smørgrav 
2651ec0d754SDag-Erling Smørgrav static void
2661ec0d754SDag-Erling Smørgrav display_loginmsg(void)
2671ec0d754SDag-Erling Smørgrav {
2681ec0d754SDag-Erling Smørgrav 	if (buffer_len(&loginmsg) > 0) {
2691ec0d754SDag-Erling Smørgrav 		buffer_append(&loginmsg, "\0", 1);
27021e764dfSDag-Erling Smørgrav 		printf("%s", (char *)buffer_ptr(&loginmsg));
2711ec0d754SDag-Erling Smørgrav 		buffer_clear(&loginmsg);
2721ec0d754SDag-Erling Smørgrav 	}
2731ec0d754SDag-Erling Smørgrav }
27480628bacSDag-Erling Smørgrav 
275ca3176e7SBrian Feldman void
276ca3176e7SBrian Feldman do_authenticated(Authctxt *authctxt)
277ca3176e7SBrian Feldman {
278e73e9afaSDag-Erling Smørgrav 	setproctitle("%s", authctxt->pw->pw_name);
279e73e9afaSDag-Erling Smørgrav 
280ca3176e7SBrian Feldman 	/* setup the channel layer */
281ca3176e7SBrian Feldman 	if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
282ca3176e7SBrian Feldman 		channel_permit_all_opens();
283ca3176e7SBrian Feldman 
284b15c8340SDag-Erling Smørgrav 	auth_debug_send();
285b15c8340SDag-Erling Smørgrav 
286ca3176e7SBrian Feldman 	if (compat20)
287ca3176e7SBrian Feldman 		do_authenticated2(authctxt);
288ca3176e7SBrian Feldman 	else
289ca3176e7SBrian Feldman 		do_authenticated1(authctxt);
290e9fd63dfSBrian Feldman 
2911ec0d754SDag-Erling Smørgrav 	do_cleanup(authctxt);
292a04a10f8SKris Kennaway }
293a04a10f8SKris Kennaway 
294a04a10f8SKris Kennaway /*
295a04a10f8SKris Kennaway  * Prepares for an interactive session.  This is called after the user has
296a04a10f8SKris Kennaway  * been successfully authenticated.  During this message exchange, pseudo
297a04a10f8SKris Kennaway  * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
298a04a10f8SKris Kennaway  * are requested, etc.
299a04a10f8SKris Kennaway  */
300af12a3e7SDag-Erling Smørgrav static void
301ca3176e7SBrian Feldman do_authenticated1(Authctxt *authctxt)
302a04a10f8SKris Kennaway {
303a04a10f8SKris Kennaway 	Session *s;
304a04a10f8SKris Kennaway 	char *command;
305af12a3e7SDag-Erling Smørgrav 	int success, type, screen_flag;
306a82e551fSDag-Erling Smørgrav 	int enable_compression_after_reply = 0;
307a82e551fSDag-Erling Smørgrav 	u_int proto_len, data_len, dlen, compression_level = 0;
308a04a10f8SKris Kennaway 
309a04a10f8SKris Kennaway 	s = session_new();
310aa49c926SDag-Erling Smørgrav 	if (s == NULL) {
311aa49c926SDag-Erling Smørgrav 		error("no more sessions");
312aa49c926SDag-Erling Smørgrav 		return;
313aa49c926SDag-Erling Smørgrav 	}
314af12a3e7SDag-Erling Smørgrav 	s->authctxt = authctxt;
315ca3176e7SBrian Feldman 	s->pw = authctxt->pw;
316c2d3a559SKris Kennaway 
317a04a10f8SKris Kennaway 	/*
318a04a10f8SKris Kennaway 	 * We stay in this loop until the client requests to execute a shell
319a04a10f8SKris Kennaway 	 * or a command.
320a04a10f8SKris Kennaway 	 */
321a04a10f8SKris Kennaway 	for (;;) {
322ca3176e7SBrian Feldman 		success = 0;
323a04a10f8SKris Kennaway 
324a04a10f8SKris Kennaway 		/* Get a packet from the client. */
325af12a3e7SDag-Erling Smørgrav 		type = packet_read();
326a04a10f8SKris Kennaway 
327a04a10f8SKris Kennaway 		/* Process the packet. */
328a04a10f8SKris Kennaway 		switch (type) {
329a04a10f8SKris Kennaway 		case SSH_CMSG_REQUEST_COMPRESSION:
330a04a10f8SKris Kennaway 			compression_level = packet_get_int();
331af12a3e7SDag-Erling Smørgrav 			packet_check_eom();
332a04a10f8SKris Kennaway 			if (compression_level < 1 || compression_level > 9) {
33321e764dfSDag-Erling Smørgrav 				packet_send_debug("Received invalid compression level %d.",
334a04a10f8SKris Kennaway 				    compression_level);
335a04a10f8SKris Kennaway 				break;
336a04a10f8SKris Kennaway 			}
337d4ecd108SDag-Erling Smørgrav 			if (options.compression == COMP_NONE) {
33880628bacSDag-Erling Smørgrav 				debug2("compression disabled");
33980628bacSDag-Erling Smørgrav 				break;
34080628bacSDag-Erling Smørgrav 			}
341a04a10f8SKris Kennaway 			/* Enable compression after we have responded with SUCCESS. */
342a04a10f8SKris Kennaway 			enable_compression_after_reply = 1;
343a04a10f8SKris Kennaway 			success = 1;
344a04a10f8SKris Kennaway 			break;
345a04a10f8SKris Kennaway 
346a04a10f8SKris Kennaway 		case SSH_CMSG_REQUEST_PTY:
347af12a3e7SDag-Erling Smørgrav 			success = session_pty_req(s);
348a04a10f8SKris Kennaway 			break;
349a04a10f8SKris Kennaway 
350a04a10f8SKris Kennaway 		case SSH_CMSG_X11_REQUEST_FORWARDING:
351a04a10f8SKris Kennaway 			s->auth_proto = packet_get_string(&proto_len);
352a04a10f8SKris Kennaway 			s->auth_data = packet_get_string(&data_len);
353a04a10f8SKris Kennaway 
354ca3176e7SBrian Feldman 			screen_flag = packet_get_protocol_flags() &
355ca3176e7SBrian Feldman 			    SSH_PROTOFLAG_SCREEN_NUMBER;
356ca3176e7SBrian Feldman 			debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag);
357ca3176e7SBrian Feldman 
358ca3176e7SBrian Feldman 			if (packet_remaining() == 4) {
359ca3176e7SBrian Feldman 				if (!screen_flag)
360ca3176e7SBrian Feldman 					debug2("Buggy client: "
361ca3176e7SBrian Feldman 					    "X11 screen flag missing");
362a04a10f8SKris Kennaway 				s->screen = packet_get_int();
363ca3176e7SBrian Feldman 			} else {
364a04a10f8SKris Kennaway 				s->screen = 0;
365ca3176e7SBrian Feldman 			}
366af12a3e7SDag-Erling Smørgrav 			packet_check_eom();
367af12a3e7SDag-Erling Smørgrav 			success = session_setup_x11fwd(s);
368af12a3e7SDag-Erling Smørgrav 			if (!success) {
369af12a3e7SDag-Erling Smørgrav 				xfree(s->auth_proto);
370af12a3e7SDag-Erling Smørgrav 				xfree(s->auth_data);
371af12a3e7SDag-Erling Smørgrav 				s->auth_proto = NULL;
372af12a3e7SDag-Erling Smørgrav 				s->auth_data = NULL;
373a04a10f8SKris Kennaway 			}
374a04a10f8SKris Kennaway 			break;
375a04a10f8SKris Kennaway 
376a04a10f8SKris Kennaway 		case SSH_CMSG_AGENT_REQUEST_FORWARDING:
377d4af9e69SDag-Erling Smørgrav 			if (!options.allow_agent_forwarding ||
378d4af9e69SDag-Erling Smørgrav 			    no_agent_forwarding_flag || compat13) {
379a04a10f8SKris Kennaway 				debug("Authentication agent forwarding not permitted for this authentication.");
380a04a10f8SKris Kennaway 				break;
381a04a10f8SKris Kennaway 			}
382a04a10f8SKris Kennaway 			debug("Received authentication agent forwarding request.");
383ca3176e7SBrian Feldman 			success = auth_input_request_forwarding(s->pw);
384a04a10f8SKris Kennaway 			break;
385a04a10f8SKris Kennaway 
386a04a10f8SKris Kennaway 		case SSH_CMSG_PORT_FORWARD_REQUEST:
387a04a10f8SKris Kennaway 			if (no_port_forwarding_flag) {
388a04a10f8SKris Kennaway 				debug("Port forwarding not permitted for this authentication.");
389a04a10f8SKris Kennaway 				break;
390a04a10f8SKris Kennaway 			}
39109958426SBrian Feldman 			if (!options.allow_tcp_forwarding) {
39209958426SBrian Feldman 				debug("Port forwarding not permitted.");
39309958426SBrian Feldman 				break;
39409958426SBrian Feldman 			}
395a04a10f8SKris Kennaway 			debug("Received TCP/IP port forwarding request.");
396333ee039SDag-Erling Smørgrav 			if (channel_input_port_forward_request(s->pw->pw_uid == 0,
397333ee039SDag-Erling Smørgrav 			    options.gateway_ports) < 0) {
398333ee039SDag-Erling Smørgrav 				debug("Port forwarding failed.");
399333ee039SDag-Erling Smørgrav 				break;
400333ee039SDag-Erling Smørgrav 			}
401a04a10f8SKris Kennaway 			success = 1;
402a04a10f8SKris Kennaway 			break;
403a04a10f8SKris Kennaway 
404a04a10f8SKris Kennaway 		case SSH_CMSG_MAX_PACKET_SIZE:
405a04a10f8SKris Kennaway 			if (packet_set_maxsize(packet_get_int()) > 0)
406a04a10f8SKris Kennaway 				success = 1;
407a04a10f8SKris Kennaway 			break;
408a04a10f8SKris Kennaway 
409a04a10f8SKris Kennaway 		case SSH_CMSG_EXEC_SHELL:
410a04a10f8SKris Kennaway 		case SSH_CMSG_EXEC_CMD:
411a04a10f8SKris Kennaway 			if (type == SSH_CMSG_EXEC_CMD) {
412a04a10f8SKris Kennaway 				command = packet_get_string(&dlen);
413a04a10f8SKris Kennaway 				debug("Exec command '%.500s'", command);
414d4af9e69SDag-Erling Smørgrav 				if (do_exec(s, command) != 0)
415d4af9e69SDag-Erling Smørgrav 					packet_disconnect(
416d4af9e69SDag-Erling Smørgrav 					    "command execution failed");
417a04a10f8SKris Kennaway 				xfree(command);
418af12a3e7SDag-Erling Smørgrav 			} else {
419d4af9e69SDag-Erling Smørgrav 				if (do_exec(s, NULL) != 0)
420d4af9e69SDag-Erling Smørgrav 					packet_disconnect(
421d4af9e69SDag-Erling Smørgrav 					    "shell execution failed");
422af12a3e7SDag-Erling Smørgrav 			}
423af12a3e7SDag-Erling Smørgrav 			packet_check_eom();
424af12a3e7SDag-Erling Smørgrav 			session_close(s);
425a04a10f8SKris Kennaway 			return;
426a04a10f8SKris Kennaway 
427a04a10f8SKris Kennaway 		default:
428a04a10f8SKris Kennaway 			/*
429a04a10f8SKris Kennaway 			 * Any unknown messages in this phase are ignored,
430a04a10f8SKris Kennaway 			 * and a failure message is returned.
431a04a10f8SKris Kennaway 			 */
432cf2b5f3bSDag-Erling Smørgrav 			logit("Unknown packet type received after authentication: %d", type);
433a04a10f8SKris Kennaway 		}
434a04a10f8SKris Kennaway 		packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
435a04a10f8SKris Kennaway 		packet_send();
436a04a10f8SKris Kennaway 		packet_write_wait();
437a04a10f8SKris Kennaway 
438a04a10f8SKris Kennaway 		/* Enable compression now that we have replied if appropriate. */
439a04a10f8SKris Kennaway 		if (enable_compression_after_reply) {
440a04a10f8SKris Kennaway 			enable_compression_after_reply = 0;
441a04a10f8SKris Kennaway 			packet_start_compression(compression_level);
442a04a10f8SKris Kennaway 		}
443a04a10f8SKris Kennaway 	}
444a04a10f8SKris Kennaway }
445a04a10f8SKris Kennaway 
446d4af9e69SDag-Erling Smørgrav #define USE_PIPES
447a04a10f8SKris Kennaway /*
448a04a10f8SKris Kennaway  * This is called to fork and execute a command when we have no tty.  This
449a04a10f8SKris Kennaway  * will call do_child from the child, and server_loop from the parent after
450a04a10f8SKris Kennaway  * setting up file descriptors and such.
451a04a10f8SKris Kennaway  */
452d4af9e69SDag-Erling Smørgrav int
453ca3176e7SBrian Feldman do_exec_no_pty(Session *s, const char *command)
454a04a10f8SKris Kennaway {
45580628bacSDag-Erling Smørgrav 	pid_t pid;
456a04a10f8SKris Kennaway 
457a04a10f8SKris Kennaway #ifdef USE_PIPES
458a04a10f8SKris Kennaway 	int pin[2], pout[2], perr[2];
459d4af9e69SDag-Erling Smørgrav 
460e2f6069cSDag-Erling Smørgrav 	if (s == NULL)
461e2f6069cSDag-Erling Smørgrav 		fatal("do_exec_no_pty: no session");
462e2f6069cSDag-Erling Smørgrav 
463a04a10f8SKris Kennaway 	/* Allocate pipes for communicating with the program. */
464d4af9e69SDag-Erling Smørgrav 	if (pipe(pin) < 0) {
465d4af9e69SDag-Erling Smørgrav 		error("%s: pipe in: %.100s", __func__, strerror(errno));
466d4af9e69SDag-Erling Smørgrav 		return -1;
467d4af9e69SDag-Erling Smørgrav 	}
468d4af9e69SDag-Erling Smørgrav 	if (pipe(pout) < 0) {
469d4af9e69SDag-Erling Smørgrav 		error("%s: pipe out: %.100s", __func__, strerror(errno));
470d4af9e69SDag-Erling Smørgrav 		close(pin[0]);
471d4af9e69SDag-Erling Smørgrav 		close(pin[1]);
472d4af9e69SDag-Erling Smørgrav 		return -1;
473d4af9e69SDag-Erling Smørgrav 	}
474d4af9e69SDag-Erling Smørgrav 	if (pipe(perr) < 0) {
475e2f6069cSDag-Erling Smørgrav 		error("%s: pipe err: %.100s", __func__,
476e2f6069cSDag-Erling Smørgrav 		    strerror(errno));
477d4af9e69SDag-Erling Smørgrav 		close(pin[0]);
478d4af9e69SDag-Erling Smørgrav 		close(pin[1]);
479d4af9e69SDag-Erling Smørgrav 		close(pout[0]);
480d4af9e69SDag-Erling Smørgrav 		close(pout[1]);
481d4af9e69SDag-Erling Smørgrav 		return -1;
482d4af9e69SDag-Erling Smørgrav 	}
483d4af9e69SDag-Erling Smørgrav #else
484a04a10f8SKris Kennaway 	int inout[2], err[2];
485d4af9e69SDag-Erling Smørgrav 
486e2f6069cSDag-Erling Smørgrav 	if (s == NULL)
487e2f6069cSDag-Erling Smørgrav 		fatal("do_exec_no_pty: no session");
488e2f6069cSDag-Erling Smørgrav 
489a04a10f8SKris Kennaway 	/* Uses socket pairs to communicate with the program. */
490d4af9e69SDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) {
491d4af9e69SDag-Erling Smørgrav 		error("%s: socketpair #1: %.100s", __func__, strerror(errno));
492d4af9e69SDag-Erling Smørgrav 		return -1;
493d4af9e69SDag-Erling Smørgrav 	}
494d4af9e69SDag-Erling Smørgrav 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) {
495e2f6069cSDag-Erling Smørgrav 		error("%s: socketpair #2: %.100s", __func__,
496e2f6069cSDag-Erling Smørgrav 		    strerror(errno));
497d4af9e69SDag-Erling Smørgrav 		close(inout[0]);
498d4af9e69SDag-Erling Smørgrav 		close(inout[1]);
499d4af9e69SDag-Erling Smørgrav 		return -1;
500d4af9e69SDag-Erling Smørgrav 	}
501d4af9e69SDag-Erling Smørgrav #endif
502d4af9e69SDag-Erling Smørgrav 
503a04a10f8SKris Kennaway 	session_proctitle(s);
504a04a10f8SKris Kennaway 
505a04a10f8SKris Kennaway 	/* Fork the child. */
506d4af9e69SDag-Erling Smørgrav 	switch ((pid = fork())) {
507d4af9e69SDag-Erling Smørgrav 	case -1:
508d4af9e69SDag-Erling Smørgrav 		error("%s: fork: %.100s", __func__, strerror(errno));
509d4af9e69SDag-Erling Smørgrav #ifdef USE_PIPES
510d4af9e69SDag-Erling Smørgrav 		close(pin[0]);
511d4af9e69SDag-Erling Smørgrav 		close(pin[1]);
512d4af9e69SDag-Erling Smørgrav 		close(pout[0]);
513d4af9e69SDag-Erling Smørgrav 		close(pout[1]);
514d4af9e69SDag-Erling Smørgrav 		close(perr[0]);
515d4af9e69SDag-Erling Smørgrav 		close(perr[1]);
516d4af9e69SDag-Erling Smørgrav #else
517d4af9e69SDag-Erling Smørgrav 		close(inout[0]);
518d4af9e69SDag-Erling Smørgrav 		close(inout[1]);
519d4af9e69SDag-Erling Smørgrav 		close(err[0]);
520d4af9e69SDag-Erling Smørgrav 		close(err[1]);
521d4af9e69SDag-Erling Smørgrav #endif
522d4af9e69SDag-Erling Smørgrav 		return -1;
523d4af9e69SDag-Erling Smørgrav 	case 0:
5241ec0d754SDag-Erling Smørgrav 		is_child = 1;
525f388f5efSDag-Erling Smørgrav 
526a04a10f8SKris Kennaway 		/* Child.  Reinitialize the log since the pid has changed. */
527d4af9e69SDag-Erling Smørgrav 		log_init(__progname, options.log_level,
528d4af9e69SDag-Erling Smørgrav 		    options.log_facility, log_stderr);
529a04a10f8SKris Kennaway 
530a04a10f8SKris Kennaway 		/*
531a04a10f8SKris Kennaway 		 * Create a new session and process group since the 4.4BSD
532a04a10f8SKris Kennaway 		 * setlogin() affects the entire process group.
533a04a10f8SKris Kennaway 		 */
534a04a10f8SKris Kennaway 		if (setsid() < 0)
535a04a10f8SKris Kennaway 			error("setsid failed: %.100s", strerror(errno));
536a04a10f8SKris Kennaway 
537a04a10f8SKris Kennaway #ifdef USE_PIPES
538a04a10f8SKris Kennaway 		/*
539a04a10f8SKris Kennaway 		 * Redirect stdin.  We close the parent side of the socket
540a04a10f8SKris Kennaway 		 * pair, and make the child side the standard input.
541a04a10f8SKris Kennaway 		 */
542a04a10f8SKris Kennaway 		close(pin[1]);
543a04a10f8SKris Kennaway 		if (dup2(pin[0], 0) < 0)
544a04a10f8SKris Kennaway 			perror("dup2 stdin");
545a04a10f8SKris Kennaway 		close(pin[0]);
546a04a10f8SKris Kennaway 
547a04a10f8SKris Kennaway 		/* Redirect stdout. */
548a04a10f8SKris Kennaway 		close(pout[0]);
549a04a10f8SKris Kennaway 		if (dup2(pout[1], 1) < 0)
550a04a10f8SKris Kennaway 			perror("dup2 stdout");
551a04a10f8SKris Kennaway 		close(pout[1]);
552a04a10f8SKris Kennaway 
553a04a10f8SKris Kennaway 		/* Redirect stderr. */
554a04a10f8SKris Kennaway 		close(perr[0]);
555a04a10f8SKris Kennaway 		if (dup2(perr[1], 2) < 0)
556a04a10f8SKris Kennaway 			perror("dup2 stderr");
557a04a10f8SKris Kennaway 		close(perr[1]);
558d4af9e69SDag-Erling Smørgrav #else
559a04a10f8SKris Kennaway 		/*
560a04a10f8SKris Kennaway 		 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
561a04a10f8SKris Kennaway 		 * use the same socket, as some programs (particularly rdist)
562a04a10f8SKris Kennaway 		 * seem to depend on it.
563a04a10f8SKris Kennaway 		 */
564a04a10f8SKris Kennaway 		close(inout[1]);
565a04a10f8SKris Kennaway 		close(err[1]);
566a04a10f8SKris Kennaway 		if (dup2(inout[0], 0) < 0)	/* stdin */
567a04a10f8SKris Kennaway 			perror("dup2 stdin");
568d4af9e69SDag-Erling Smørgrav 		if (dup2(inout[0], 1) < 0)	/* stdout (same as stdin) */
569a04a10f8SKris Kennaway 			perror("dup2 stdout");
570d4af9e69SDag-Erling Smørgrav 		close(inout[0]);
571a04a10f8SKris Kennaway 		if (dup2(err[0], 2) < 0)	/* stderr */
572a04a10f8SKris Kennaway 			perror("dup2 stderr");
573d4af9e69SDag-Erling Smørgrav 		close(err[0]);
574d4af9e69SDag-Erling Smørgrav #endif
575d4af9e69SDag-Erling Smørgrav 
576a04a10f8SKris Kennaway 
577f388f5efSDag-Erling Smørgrav #ifdef _UNICOS
578f388f5efSDag-Erling Smørgrav 		cray_init_job(s->pw); /* set up cray jid and tmpdir */
579f388f5efSDag-Erling Smørgrav #endif
580f388f5efSDag-Erling Smørgrav 
581a04a10f8SKris Kennaway 		/* Do processing for the child (exec command etc). */
582ca3176e7SBrian Feldman 		do_child(s, command);
583a04a10f8SKris Kennaway 		/* NOTREACHED */
584d4af9e69SDag-Erling Smørgrav 	default:
585d4af9e69SDag-Erling Smørgrav 		break;
586a04a10f8SKris Kennaway 	}
587d4af9e69SDag-Erling Smørgrav 
588f388f5efSDag-Erling Smørgrav #ifdef _UNICOS
589f388f5efSDag-Erling Smørgrav 	signal(WJSIGNAL, cray_job_termination_handler);
590f388f5efSDag-Erling Smørgrav #endif /* _UNICOS */
591989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
592989dd127SDag-Erling Smørgrav 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
593989dd127SDag-Erling Smørgrav #endif
594d4af9e69SDag-Erling Smørgrav 
595a04a10f8SKris Kennaway 	s->pid = pid;
596ca3176e7SBrian Feldman 	/* Set interactive/non-interactive mode. */
5974a421b63SDag-Erling Smørgrav 	packet_set_interactive(s->display != NULL,
5984a421b63SDag-Erling Smørgrav 	    options.ip_qos_interactive, options.ip_qos_bulk);
599d4af9e69SDag-Erling Smørgrav 
600d4af9e69SDag-Erling Smørgrav 	/*
601d4af9e69SDag-Erling Smørgrav 	 * Clear loginmsg, since it's the child's responsibility to display
602d4af9e69SDag-Erling Smørgrav 	 * it to the user, otherwise multiple sessions may accumulate
603d4af9e69SDag-Erling Smørgrav 	 * multiple copies of the login messages.
604d4af9e69SDag-Erling Smørgrav 	 */
605d4af9e69SDag-Erling Smørgrav 	buffer_clear(&loginmsg);
606d4af9e69SDag-Erling Smørgrav 
607a04a10f8SKris Kennaway #ifdef USE_PIPES
608a04a10f8SKris Kennaway 	/* We are the parent.  Close the child sides of the pipes. */
609a04a10f8SKris Kennaway 	close(pin[0]);
610a04a10f8SKris Kennaway 	close(pout[1]);
611a04a10f8SKris Kennaway 	close(perr[1]);
612a04a10f8SKris Kennaway 
613a04a10f8SKris Kennaway 	if (compat20) {
614e2f6069cSDag-Erling Smørgrav 		session_set_fds(s, pin[1], pout[0], perr[0],
615e2f6069cSDag-Erling Smørgrav 		    s->is_subsystem, 0);
616a04a10f8SKris Kennaway 	} else {
617a04a10f8SKris Kennaway 		/* Enter the interactive session. */
618a04a10f8SKris Kennaway 		server_loop(pid, pin[1], pout[0], perr[0]);
619ca3176e7SBrian Feldman 		/* server_loop has closed pin[1], pout[0], and perr[0]. */
620a04a10f8SKris Kennaway 	}
621d4af9e69SDag-Erling Smørgrav #else
622a04a10f8SKris Kennaway 	/* We are the parent.  Close the child sides of the socket pairs. */
623a04a10f8SKris Kennaway 	close(inout[0]);
624a04a10f8SKris Kennaway 	close(err[0]);
625a04a10f8SKris Kennaway 
626a04a10f8SKris Kennaway 	/*
627a04a10f8SKris Kennaway 	 * Enter the interactive session.  Note: server_loop must be able to
628a04a10f8SKris Kennaway 	 * handle the case that fdin and fdout are the same.
629a04a10f8SKris Kennaway 	 */
630a04a10f8SKris Kennaway 	if (compat20) {
631e2f6069cSDag-Erling Smørgrav 		session_set_fds(s, inout[1], inout[1], err[1],
632e2f6069cSDag-Erling Smørgrav 		    s->is_subsystem, 0);
633a04a10f8SKris Kennaway 	} else {
634a04a10f8SKris Kennaway 		server_loop(pid, inout[1], inout[1], err[1]);
635a04a10f8SKris Kennaway 		/* server_loop has closed inout[1] and err[1]. */
636a04a10f8SKris Kennaway 	}
637d4af9e69SDag-Erling Smørgrav #endif
638d4af9e69SDag-Erling Smørgrav 	return 0;
639a04a10f8SKris Kennaway }
640a04a10f8SKris Kennaway 
641a04a10f8SKris Kennaway /*
642a04a10f8SKris Kennaway  * This is called to fork and execute a command when we have a tty.  This
643a04a10f8SKris Kennaway  * will call do_child from the child, and server_loop from the parent after
644a04a10f8SKris Kennaway  * setting up file descriptors, controlling tty, updating wtmp, utmp,
645a04a10f8SKris Kennaway  * lastlog, and other such operations.
646a04a10f8SKris Kennaway  */
647d4af9e69SDag-Erling Smørgrav int
648ca3176e7SBrian Feldman do_exec_pty(Session *s, const char *command)
649a04a10f8SKris Kennaway {
650a04a10f8SKris Kennaway 	int fdout, ptyfd, ttyfd, ptymaster;
651a04a10f8SKris Kennaway 	pid_t pid;
652a04a10f8SKris Kennaway 
653a04a10f8SKris Kennaway 	if (s == NULL)
654a04a10f8SKris Kennaway 		fatal("do_exec_pty: no session");
655a04a10f8SKris Kennaway 	ptyfd = s->ptyfd;
656a04a10f8SKris Kennaway 	ttyfd = s->ttyfd;
657a04a10f8SKris Kennaway 
658d4af9e69SDag-Erling Smørgrav 	/*
659d4af9e69SDag-Erling Smørgrav 	 * Create another descriptor of the pty master side for use as the
660d4af9e69SDag-Erling Smørgrav 	 * standard input.  We could use the original descriptor, but this
661d4af9e69SDag-Erling Smørgrav 	 * simplifies code in server_loop.  The descriptor is bidirectional.
662d4af9e69SDag-Erling Smørgrav 	 * Do this before forking (and cleanup in the child) so as to
663d4af9e69SDag-Erling Smørgrav 	 * detect and gracefully fail out-of-fd conditions.
664d4af9e69SDag-Erling Smørgrav 	 */
665d4af9e69SDag-Erling Smørgrav 	if ((fdout = dup(ptyfd)) < 0) {
666d4af9e69SDag-Erling Smørgrav 		error("%s: dup #1: %s", __func__, strerror(errno));
667d4af9e69SDag-Erling Smørgrav 		close(ttyfd);
668d4af9e69SDag-Erling Smørgrav 		close(ptyfd);
669d4af9e69SDag-Erling Smørgrav 		return -1;
670cf2b5f3bSDag-Erling Smørgrav 	}
671d4af9e69SDag-Erling Smørgrav 	/* we keep a reference to the pty master */
672d4af9e69SDag-Erling Smørgrav 	if ((ptymaster = dup(ptyfd)) < 0) {
673d4af9e69SDag-Erling Smørgrav 		error("%s: dup #2: %s", __func__, strerror(errno));
674d4af9e69SDag-Erling Smørgrav 		close(ttyfd);
675d4af9e69SDag-Erling Smørgrav 		close(ptyfd);
676d4af9e69SDag-Erling Smørgrav 		close(fdout);
677d4af9e69SDag-Erling Smørgrav 		return -1;
678d4af9e69SDag-Erling Smørgrav 	}
67909958426SBrian Feldman 
680a04a10f8SKris Kennaway 	/* Fork the child. */
681d4af9e69SDag-Erling Smørgrav 	switch ((pid = fork())) {
682d4af9e69SDag-Erling Smørgrav 	case -1:
683d4af9e69SDag-Erling Smørgrav 		error("%s: fork: %.100s", __func__, strerror(errno));
684d4af9e69SDag-Erling Smørgrav 		close(fdout);
685d4af9e69SDag-Erling Smørgrav 		close(ptymaster);
686d4af9e69SDag-Erling Smørgrav 		close(ttyfd);
687d4af9e69SDag-Erling Smørgrav 		close(ptyfd);
688d4af9e69SDag-Erling Smørgrav 		return -1;
689d4af9e69SDag-Erling Smørgrav 	case 0:
6901ec0d754SDag-Erling Smørgrav 		is_child = 1;
691af12a3e7SDag-Erling Smørgrav 
692d4af9e69SDag-Erling Smørgrav 		close(fdout);
693d4af9e69SDag-Erling Smørgrav 		close(ptymaster);
694d4af9e69SDag-Erling Smørgrav 
695c2d3a559SKris Kennaway 		/* Child.  Reinitialize the log because the pid has changed. */
696d4af9e69SDag-Erling Smørgrav 		log_init(__progname, options.log_level,
697d4af9e69SDag-Erling Smørgrav 		    options.log_facility, log_stderr);
698a04a10f8SKris Kennaway 		/* Close the master side of the pseudo tty. */
699a04a10f8SKris Kennaway 		close(ptyfd);
700a04a10f8SKris Kennaway 
701a04a10f8SKris Kennaway 		/* Make the pseudo tty our controlling tty. */
702a04a10f8SKris Kennaway 		pty_make_controlling_tty(&ttyfd, s->tty);
703a04a10f8SKris Kennaway 
704af12a3e7SDag-Erling Smørgrav 		/* Redirect stdin/stdout/stderr from the pseudo tty. */
705af12a3e7SDag-Erling Smørgrav 		if (dup2(ttyfd, 0) < 0)
706af12a3e7SDag-Erling Smørgrav 			error("dup2 stdin: %s", strerror(errno));
707af12a3e7SDag-Erling Smørgrav 		if (dup2(ttyfd, 1) < 0)
708af12a3e7SDag-Erling Smørgrav 			error("dup2 stdout: %s", strerror(errno));
709af12a3e7SDag-Erling Smørgrav 		if (dup2(ttyfd, 2) < 0)
710af12a3e7SDag-Erling Smørgrav 			error("dup2 stderr: %s", strerror(errno));
711a04a10f8SKris Kennaway 
712a04a10f8SKris Kennaway 		/* Close the extra descriptor for the pseudo tty. */
713a04a10f8SKris Kennaway 		close(ttyfd);
714a04a10f8SKris Kennaway 
715c2d3a559SKris Kennaway 		/* record login, etc. similar to login(1) */
716989dd127SDag-Erling Smørgrav #ifndef HAVE_OSF_SIA
717f388f5efSDag-Erling Smørgrav 		if (!(options.use_login && command == NULL)) {
718f388f5efSDag-Erling Smørgrav #ifdef _UNICOS
719f388f5efSDag-Erling Smørgrav 			cray_init_job(s->pw); /* set up cray jid and tmpdir */
720f388f5efSDag-Erling Smørgrav #endif /* _UNICOS */
721ca3176e7SBrian Feldman 			do_login(s, command);
722f388f5efSDag-Erling Smørgrav 		}
723989dd127SDag-Erling Smørgrav # ifdef LOGIN_NEEDS_UTMPX
724989dd127SDag-Erling Smørgrav 		else
725989dd127SDag-Erling Smørgrav 			do_pre_login(s);
726989dd127SDag-Erling Smørgrav # endif
727989dd127SDag-Erling Smørgrav #endif
728d4af9e69SDag-Erling Smørgrav 		/*
729d4af9e69SDag-Erling Smørgrav 		 * Do common processing for the child, such as execing
730d4af9e69SDag-Erling Smørgrav 		 * the command.
731d4af9e69SDag-Erling Smørgrav 		 */
732ca3176e7SBrian Feldman 		do_child(s, command);
733a04a10f8SKris Kennaway 		/* NOTREACHED */
734d4af9e69SDag-Erling Smørgrav 	default:
735d4af9e69SDag-Erling Smørgrav 		break;
736a04a10f8SKris Kennaway 	}
737d4af9e69SDag-Erling Smørgrav 
738f388f5efSDag-Erling Smørgrav #ifdef _UNICOS
739f388f5efSDag-Erling Smørgrav 	signal(WJSIGNAL, cray_job_termination_handler);
740f388f5efSDag-Erling Smørgrav #endif /* _UNICOS */
741989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
742989dd127SDag-Erling Smørgrav 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
743989dd127SDag-Erling Smørgrav #endif
744d4af9e69SDag-Erling Smørgrav 
745a04a10f8SKris Kennaway 	s->pid = pid;
746a04a10f8SKris Kennaway 
747a04a10f8SKris Kennaway 	/* Parent.  Close the slave side of the pseudo tty. */
748a04a10f8SKris Kennaway 	close(ttyfd);
749a04a10f8SKris Kennaway 
750a04a10f8SKris Kennaway 	/* Enter interactive session. */
751d4af9e69SDag-Erling Smørgrav 	s->ptymaster = ptymaster;
7524a421b63SDag-Erling Smørgrav 	packet_set_interactive(1,
7534a421b63SDag-Erling Smørgrav 	    options.ip_qos_interactive, options.ip_qos_bulk);
754a04a10f8SKris Kennaway 	if (compat20) {
755e2f6069cSDag-Erling Smørgrav 		session_set_fds(s, ptyfd, fdout, -1, 1, 1);
756a04a10f8SKris Kennaway 	} else {
757a04a10f8SKris Kennaway 		server_loop(pid, ptyfd, fdout, -1);
758a04a10f8SKris Kennaway 		/* server_loop _has_ closed ptyfd and fdout. */
759a04a10f8SKris Kennaway 	}
760d4af9e69SDag-Erling Smørgrav 	return 0;
761a04a10f8SKris Kennaway }
762a04a10f8SKris Kennaway 
763989dd127SDag-Erling Smørgrav #ifdef LOGIN_NEEDS_UTMPX
764989dd127SDag-Erling Smørgrav static void
765989dd127SDag-Erling Smørgrav do_pre_login(Session *s)
766989dd127SDag-Erling Smørgrav {
767989dd127SDag-Erling Smørgrav 	socklen_t fromlen;
768989dd127SDag-Erling Smørgrav 	struct sockaddr_storage from;
769989dd127SDag-Erling Smørgrav 	pid_t pid = getpid();
770989dd127SDag-Erling Smørgrav 
771989dd127SDag-Erling Smørgrav 	/*
772989dd127SDag-Erling Smørgrav 	 * Get IP address of client. If the connection is not a socket, let
773989dd127SDag-Erling Smørgrav 	 * the address be 0.0.0.0.
774989dd127SDag-Erling Smørgrav 	 */
775989dd127SDag-Erling Smørgrav 	memset(&from, 0, sizeof(from));
776989dd127SDag-Erling Smørgrav 	fromlen = sizeof(from);
7777ac32603SHajimu UMEMOTO 	if (packet_connection_is_on_socket()) {
778989dd127SDag-Erling Smørgrav 		if (getpeername(packet_get_connection_in(),
779989dd127SDag-Erling Smørgrav 		    (struct sockaddr *)&from, &fromlen) < 0) {
780989dd127SDag-Erling Smørgrav 			debug("getpeername: %.100s", strerror(errno));
7811ec0d754SDag-Erling Smørgrav 			cleanup_exit(255);
782989dd127SDag-Erling Smørgrav 		}
783989dd127SDag-Erling Smørgrav 	}
784989dd127SDag-Erling Smørgrav 
785989dd127SDag-Erling Smørgrav 	record_utmp_only(pid, s->tty, s->pw->pw_name,
786cf2b5f3bSDag-Erling Smørgrav 	    get_remote_name_or_ip(utmp_len, options.use_dns),
7877ac32603SHajimu UMEMOTO 	    (struct sockaddr *)&from, fromlen);
788989dd127SDag-Erling Smørgrav }
789989dd127SDag-Erling Smørgrav #endif
790989dd127SDag-Erling Smørgrav 
791af12a3e7SDag-Erling Smørgrav /*
792af12a3e7SDag-Erling Smørgrav  * This is called to fork and execute a command.  If another command is
793af12a3e7SDag-Erling Smørgrav  * to be forced, execute that instead.
794af12a3e7SDag-Erling Smørgrav  */
795d4af9e69SDag-Erling Smørgrav int
796af12a3e7SDag-Erling Smørgrav do_exec(Session *s, const char *command)
797af12a3e7SDag-Erling Smørgrav {
798d4af9e69SDag-Erling Smørgrav 	int ret;
799d4af9e69SDag-Erling Smørgrav 
800333ee039SDag-Erling Smørgrav 	if (options.adm_forced_command) {
801333ee039SDag-Erling Smørgrav 		original_command = command;
802333ee039SDag-Erling Smørgrav 		command = options.adm_forced_command;
803b15c8340SDag-Erling Smørgrav 		if (IS_INTERNAL_SFTP(command)) {
804b15c8340SDag-Erling Smørgrav 			s->is_subsystem = s->is_subsystem ?
805b15c8340SDag-Erling Smørgrav 			    SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
806b15c8340SDag-Erling Smørgrav 		} else if (s->is_subsystem)
807d4af9e69SDag-Erling Smørgrav 			s->is_subsystem = SUBSYSTEM_EXT;
808333ee039SDag-Erling Smørgrav 		debug("Forced command (config) '%.900s'", command);
809333ee039SDag-Erling Smørgrav 	} else if (forced_command) {
810af12a3e7SDag-Erling Smørgrav 		original_command = command;
811af12a3e7SDag-Erling Smørgrav 		command = forced_command;
812b15c8340SDag-Erling Smørgrav 		if (IS_INTERNAL_SFTP(command)) {
813b15c8340SDag-Erling Smørgrav 			s->is_subsystem = s->is_subsystem ?
814b15c8340SDag-Erling Smørgrav 			    SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
815b15c8340SDag-Erling Smørgrav 		} else if (s->is_subsystem)
816d4af9e69SDag-Erling Smørgrav 			s->is_subsystem = SUBSYSTEM_EXT;
817333ee039SDag-Erling Smørgrav 		debug("Forced command (key option) '%.900s'", command);
818af12a3e7SDag-Erling Smørgrav 	}
819af12a3e7SDag-Erling Smørgrav 
820aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS
821aa49c926SDag-Erling Smørgrav 	if (command != NULL)
822aa49c926SDag-Erling Smørgrav 		PRIVSEP(audit_run_command(command));
823aa49c926SDag-Erling Smørgrav 	else if (s->ttyfd == -1) {
824aa49c926SDag-Erling Smørgrav 		char *shell = s->pw->pw_shell;
825aa49c926SDag-Erling Smørgrav 
826aa49c926SDag-Erling Smørgrav 		if (shell[0] == '\0')	/* empty shell means /bin/sh */
827aa49c926SDag-Erling Smørgrav 			shell =_PATH_BSHELL;
828aa49c926SDag-Erling Smørgrav 		PRIVSEP(audit_run_command(shell));
829cf2b5f3bSDag-Erling Smørgrav 	}
830cf2b5f3bSDag-Erling Smørgrav #endif
831af12a3e7SDag-Erling Smørgrav 	if (s->ttyfd != -1)
832d4af9e69SDag-Erling Smørgrav 		ret = do_exec_pty(s, command);
833af12a3e7SDag-Erling Smørgrav 	else
834d4af9e69SDag-Erling Smørgrav 		ret = do_exec_no_pty(s, command);
835af12a3e7SDag-Erling Smørgrav 
836af12a3e7SDag-Erling Smørgrav 	original_command = NULL;
837af12a3e7SDag-Erling Smørgrav 
83821e764dfSDag-Erling Smørgrav 	/*
83921e764dfSDag-Erling Smørgrav 	 * Clear loginmsg: it's the child's responsibility to display
84021e764dfSDag-Erling Smørgrav 	 * it to the user, otherwise multiple sessions may accumulate
84121e764dfSDag-Erling Smørgrav 	 * multiple copies of the login messages.
84221e764dfSDag-Erling Smørgrav 	 */
84321e764dfSDag-Erling Smørgrav 	buffer_clear(&loginmsg);
844d4af9e69SDag-Erling Smørgrav 
845d4af9e69SDag-Erling Smørgrav 	return ret;
84621e764dfSDag-Erling Smørgrav }
847af12a3e7SDag-Erling Smørgrav 
848c2d3a559SKris Kennaway /* administrative, login(1)-like work */
849ca3176e7SBrian Feldman void
850c2d3a559SKris Kennaway do_login(Session *s, const char *command)
851c2d3a559SKris Kennaway {
852c2d3a559SKris Kennaway 	socklen_t fromlen;
853c2d3a559SKris Kennaway 	struct sockaddr_storage from;
854c2d3a559SKris Kennaway 	struct passwd * pw = s->pw;
855c2d3a559SKris Kennaway 	pid_t pid = getpid();
856c2d3a559SKris Kennaway 
857c2d3a559SKris Kennaway 	/*
858c2d3a559SKris Kennaway 	 * Get IP address of client. If the connection is not a socket, let
859c2d3a559SKris Kennaway 	 * the address be 0.0.0.0.
860c2d3a559SKris Kennaway 	 */
861c2d3a559SKris Kennaway 	memset(&from, 0, sizeof(from));
862c2d3a559SKris Kennaway 	fromlen = sizeof(from);
8637ac32603SHajimu UMEMOTO 	if (packet_connection_is_on_socket()) {
864c2d3a559SKris Kennaway 		if (getpeername(packet_get_connection_in(),
865c2d3a559SKris Kennaway 		    (struct sockaddr *)&from, &fromlen) < 0) {
866c2d3a559SKris Kennaway 			debug("getpeername: %.100s", strerror(errno));
8671ec0d754SDag-Erling Smørgrav 			cleanup_exit(255);
868c2d3a559SKris Kennaway 		}
869c2d3a559SKris Kennaway 	}
870c2d3a559SKris Kennaway 
87180628bacSDag-Erling Smørgrav 	/* Record that there was a login on that tty from the remote host. */
87280628bacSDag-Erling Smørgrav 	if (!use_privsep)
87380628bacSDag-Erling Smørgrav 		record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
87480628bacSDag-Erling Smørgrav 		    get_remote_name_or_ip(utmp_len,
875cf2b5f3bSDag-Erling Smørgrav 		    options.use_dns),
8767ac32603SHajimu UMEMOTO 		    (struct sockaddr *)&from, fromlen);
87780628bacSDag-Erling Smørgrav 
878098de0c1SAndrey A. Chernov #ifdef USE_PAM
879c2d3a559SKris Kennaway 	/*
880989dd127SDag-Erling Smørgrav 	 * If password change is needed, do it now.
881989dd127SDag-Erling Smørgrav 	 * This needs to occur before the ~/.hushlogin check.
882c2d3a559SKris Kennaway 	 */
8831ec0d754SDag-Erling Smørgrav 	if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) {
8841ec0d754SDag-Erling Smørgrav 		display_loginmsg();
885989dd127SDag-Erling Smørgrav 		do_pam_chauthtok();
8861ec0d754SDag-Erling Smørgrav 		s->authctxt->force_pwchange = 0;
887cf2b5f3bSDag-Erling Smørgrav 		/* XXX - signal [net] parent to enable forwardings */
888989dd127SDag-Erling Smørgrav 	}
889989dd127SDag-Erling Smørgrav #endif
890af12a3e7SDag-Erling Smørgrav 
891989dd127SDag-Erling Smørgrav 	if (check_quietlogin(s, command))
892989dd127SDag-Erling Smørgrav 		return;
893989dd127SDag-Erling Smørgrav 
8941ec0d754SDag-Erling Smørgrav 	display_loginmsg();
895f388f5efSDag-Erling Smørgrav 
896ca3176e7SBrian Feldman 	do_motd();
897ca3176e7SBrian Feldman }
898ca3176e7SBrian Feldman 
899ca3176e7SBrian Feldman /*
900ca3176e7SBrian Feldman  * Display the message of the day.
901ca3176e7SBrian Feldman  */
902ca3176e7SBrian Feldman void
903ca3176e7SBrian Feldman do_motd(void)
904ca3176e7SBrian Feldman {
905ca3176e7SBrian Feldman 	FILE *f;
906ca3176e7SBrian Feldman 	char buf[256];
907ca3176e7SBrian Feldman 
908ca3176e7SBrian Feldman 	if (options.print_motd) {
909c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
910c2d3a559SKris Kennaway 		f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
911c2d3a559SKris Kennaway 		    "/etc/motd"), "r");
912af12a3e7SDag-Erling Smørgrav #else
913c2d3a559SKris Kennaway 		f = fopen("/etc/motd", "r");
914af12a3e7SDag-Erling Smørgrav #endif
915c2d3a559SKris Kennaway 		if (f) {
916c2d3a559SKris Kennaway 			while (fgets(buf, sizeof(buf), f))
917c2d3a559SKris Kennaway 				fputs(buf, stdout);
918c2d3a559SKris Kennaway 			fclose(f);
919c2d3a559SKris Kennaway 		}
920c2d3a559SKris Kennaway 	}
921ca3176e7SBrian Feldman }
922c2d3a559SKris Kennaway 
923af12a3e7SDag-Erling Smørgrav 
924ca3176e7SBrian Feldman /*
925ca3176e7SBrian Feldman  * Check for quiet login, either .hushlogin or command given.
926ca3176e7SBrian Feldman  */
927ca3176e7SBrian Feldman int
928ca3176e7SBrian Feldman check_quietlogin(Session *s, const char *command)
929ca3176e7SBrian Feldman {
930ca3176e7SBrian Feldman 	char buf[256];
931ca3176e7SBrian Feldman 	struct passwd *pw = s->pw;
932ca3176e7SBrian Feldman 	struct stat st;
933ca3176e7SBrian Feldman 
934ca3176e7SBrian Feldman 	/* Return 1 if .hushlogin exists or a command given. */
935ca3176e7SBrian Feldman 	if (command != NULL)
936ca3176e7SBrian Feldman 		return 1;
937ca3176e7SBrian Feldman 	snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
938c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
939ca3176e7SBrian Feldman 	if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
940ca3176e7SBrian Feldman 		return 1;
941ca3176e7SBrian Feldman #else
942ca3176e7SBrian Feldman 	if (stat(buf, &st) >= 0)
943ca3176e7SBrian Feldman 		return 1;
944ca3176e7SBrian Feldman #endif
945ca3176e7SBrian Feldman 	return 0;
946c2d3a559SKris Kennaway }
947c2d3a559SKris Kennaway 
948a04a10f8SKris Kennaway /*
949a04a10f8SKris Kennaway  * Sets the value of the given variable in the environment.  If the variable
950cce7d346SDag-Erling Smørgrav  * already exists, its value is overridden.
951a04a10f8SKris Kennaway  */
952cf2b5f3bSDag-Erling Smørgrav void
953ca3176e7SBrian Feldman child_set_env(char ***envp, u_int *envsizep, const char *name,
954a04a10f8SKris Kennaway 	const char *value)
955a04a10f8SKris Kennaway {
956a04a10f8SKris Kennaway 	char **env;
957b69cd7f2SJacques Vidrine 	u_int envsize;
958b69cd7f2SJacques Vidrine 	u_int i, namelen;
959a04a10f8SKris Kennaway 
960a04a10f8SKris Kennaway 	/*
961cf2b5f3bSDag-Erling Smørgrav 	 * If we're passed an uninitialized list, allocate a single null
962cf2b5f3bSDag-Erling Smørgrav 	 * entry before continuing.
963cf2b5f3bSDag-Erling Smørgrav 	 */
964cf2b5f3bSDag-Erling Smørgrav 	if (*envp == NULL && *envsizep == 0) {
965cf2b5f3bSDag-Erling Smørgrav 		*envp = xmalloc(sizeof(char *));
966cf2b5f3bSDag-Erling Smørgrav 		*envp[0] = NULL;
967cf2b5f3bSDag-Erling Smørgrav 		*envsizep = 1;
968cf2b5f3bSDag-Erling Smørgrav 	}
969cf2b5f3bSDag-Erling Smørgrav 
970cf2b5f3bSDag-Erling Smørgrav 	/*
971a04a10f8SKris Kennaway 	 * Find the slot where the value should be stored.  If the variable
972a04a10f8SKris Kennaway 	 * already exists, we reuse the slot; otherwise we append a new slot
973a04a10f8SKris Kennaway 	 * at the end of the array, expanding if necessary.
974a04a10f8SKris Kennaway 	 */
975a04a10f8SKris Kennaway 	env = *envp;
976a04a10f8SKris Kennaway 	namelen = strlen(name);
977a04a10f8SKris Kennaway 	for (i = 0; env[i]; i++)
978a04a10f8SKris Kennaway 		if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
979a04a10f8SKris Kennaway 			break;
980a04a10f8SKris Kennaway 	if (env[i]) {
981a04a10f8SKris Kennaway 		/* Reuse the slot. */
982a04a10f8SKris Kennaway 		xfree(env[i]);
983a04a10f8SKris Kennaway 	} else {
984a04a10f8SKris Kennaway 		/* New variable.  Expand if necessary. */
985b69cd7f2SJacques Vidrine 		envsize = *envsizep;
986b69cd7f2SJacques Vidrine 		if (i >= envsize - 1) {
987b69cd7f2SJacques Vidrine 			if (envsize >= 1000)
988b69cd7f2SJacques Vidrine 				fatal("child_set_env: too many env vars");
989b69cd7f2SJacques Vidrine 			envsize += 50;
990333ee039SDag-Erling Smørgrav 			env = (*envp) = xrealloc(env, envsize, sizeof(char *));
991b69cd7f2SJacques Vidrine 			*envsizep = envsize;
992a04a10f8SKris Kennaway 		}
993a04a10f8SKris Kennaway 		/* Need to set the NULL pointer at end of array beyond the new slot. */
994a04a10f8SKris Kennaway 		env[i + 1] = NULL;
995a04a10f8SKris Kennaway 	}
996a04a10f8SKris Kennaway 
997a04a10f8SKris Kennaway 	/* Allocate space and format the variable in the appropriate slot. */
998a04a10f8SKris Kennaway 	env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
999a04a10f8SKris Kennaway 	snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
1000a04a10f8SKris Kennaway }
1001a04a10f8SKris Kennaway 
1002a04a10f8SKris Kennaway /*
1003a04a10f8SKris Kennaway  * Reads environment variables from the given file and adds/overrides them
1004a04a10f8SKris Kennaway  * into the environment.  If the file does not exist, this does nothing.
1005a04a10f8SKris Kennaway  * Otherwise, it must consist of empty lines, comments (line starts with '#')
1006a04a10f8SKris Kennaway  * and assignments of the form name=value.  No other forms are allowed.
1007a04a10f8SKris Kennaway  */
1008af12a3e7SDag-Erling Smørgrav static void
1009ca3176e7SBrian Feldman read_environment_file(char ***env, u_int *envsize,
1010a04a10f8SKris Kennaway 	const char *filename)
1011a04a10f8SKris Kennaway {
1012a04a10f8SKris Kennaway 	FILE *f;
1013a04a10f8SKris Kennaway 	char buf[4096];
1014a04a10f8SKris Kennaway 	char *cp, *value;
1015a82e551fSDag-Erling Smørgrav 	u_int lineno = 0;
1016a04a10f8SKris Kennaway 
1017a04a10f8SKris Kennaway 	f = fopen(filename, "r");
1018a04a10f8SKris Kennaway 	if (!f)
1019a04a10f8SKris Kennaway 		return;
1020a04a10f8SKris Kennaway 
1021a04a10f8SKris Kennaway 	while (fgets(buf, sizeof(buf), f)) {
1022a82e551fSDag-Erling Smørgrav 		if (++lineno > 1000)
1023a82e551fSDag-Erling Smørgrav 			fatal("Too many lines in environment file %s", filename);
1024a04a10f8SKris Kennaway 		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
1025a04a10f8SKris Kennaway 			;
1026a04a10f8SKris Kennaway 		if (!*cp || *cp == '#' || *cp == '\n')
1027a04a10f8SKris Kennaway 			continue;
1028d4af9e69SDag-Erling Smørgrav 
1029d4af9e69SDag-Erling Smørgrav 		cp[strcspn(cp, "\n")] = '\0';
1030d4af9e69SDag-Erling Smørgrav 
1031a04a10f8SKris Kennaway 		value = strchr(cp, '=');
1032a04a10f8SKris Kennaway 		if (value == NULL) {
1033a82e551fSDag-Erling Smørgrav 			fprintf(stderr, "Bad line %u in %.100s\n", lineno,
1034a82e551fSDag-Erling Smørgrav 			    filename);
1035a04a10f8SKris Kennaway 			continue;
1036a04a10f8SKris Kennaway 		}
1037db1cb46cSKris Kennaway 		/*
1038db1cb46cSKris Kennaway 		 * Replace the equals sign by nul, and advance value to
1039db1cb46cSKris Kennaway 		 * the value string.
1040db1cb46cSKris Kennaway 		 */
1041a04a10f8SKris Kennaway 		*value = '\0';
1042a04a10f8SKris Kennaway 		value++;
1043a04a10f8SKris Kennaway 		child_set_env(env, envsize, cp, value);
1044a04a10f8SKris Kennaway 	}
1045a04a10f8SKris Kennaway 	fclose(f);
1046a04a10f8SKris Kennaway }
1047a04a10f8SKris Kennaway 
1048cf2b5f3bSDag-Erling Smørgrav #ifdef HAVE_ETC_DEFAULT_LOGIN
1049cf2b5f3bSDag-Erling Smørgrav /*
1050cf2b5f3bSDag-Erling Smørgrav  * Return named variable from specified environment, or NULL if not present.
1051cf2b5f3bSDag-Erling Smørgrav  */
1052cf2b5f3bSDag-Erling Smørgrav static char *
1053cf2b5f3bSDag-Erling Smørgrav child_get_env(char **env, const char *name)
1054cf2b5f3bSDag-Erling Smørgrav {
1055cf2b5f3bSDag-Erling Smørgrav 	int i;
1056cf2b5f3bSDag-Erling Smørgrav 	size_t len;
1057cf2b5f3bSDag-Erling Smørgrav 
1058cf2b5f3bSDag-Erling Smørgrav 	len = strlen(name);
1059cf2b5f3bSDag-Erling Smørgrav 	for (i=0; env[i] != NULL; i++)
1060cf2b5f3bSDag-Erling Smørgrav 		if (strncmp(name, env[i], len) == 0 && env[i][len] == '=')
1061cf2b5f3bSDag-Erling Smørgrav 			return(env[i] + len + 1);
1062cf2b5f3bSDag-Erling Smørgrav 	return NULL;
1063cf2b5f3bSDag-Erling Smørgrav }
1064cf2b5f3bSDag-Erling Smørgrav 
1065cf2b5f3bSDag-Erling Smørgrav /*
1066cf2b5f3bSDag-Erling Smørgrav  * Read /etc/default/login.
1067cf2b5f3bSDag-Erling Smørgrav  * We pick up the PATH (or SUPATH for root) and UMASK.
1068cf2b5f3bSDag-Erling Smørgrav  */
1069cf2b5f3bSDag-Erling Smørgrav static void
1070cf2b5f3bSDag-Erling Smørgrav read_etc_default_login(char ***env, u_int *envsize, uid_t uid)
1071cf2b5f3bSDag-Erling Smørgrav {
1072cf2b5f3bSDag-Erling Smørgrav 	char **tmpenv = NULL, *var;
1073cf2b5f3bSDag-Erling Smørgrav 	u_int i, tmpenvsize = 0;
10741ec0d754SDag-Erling Smørgrav 	u_long mask;
1075cf2b5f3bSDag-Erling Smørgrav 
1076cf2b5f3bSDag-Erling Smørgrav 	/*
1077cf2b5f3bSDag-Erling Smørgrav 	 * We don't want to copy the whole file to the child's environment,
1078cf2b5f3bSDag-Erling Smørgrav 	 * so we use a temporary environment and copy the variables we're
1079cf2b5f3bSDag-Erling Smørgrav 	 * interested in.
1080cf2b5f3bSDag-Erling Smørgrav 	 */
1081cf2b5f3bSDag-Erling Smørgrav 	read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login");
1082cf2b5f3bSDag-Erling Smørgrav 
1083cf2b5f3bSDag-Erling Smørgrav 	if (tmpenv == NULL)
1084cf2b5f3bSDag-Erling Smørgrav 		return;
1085cf2b5f3bSDag-Erling Smørgrav 
1086cf2b5f3bSDag-Erling Smørgrav 	if (uid == 0)
1087cf2b5f3bSDag-Erling Smørgrav 		var = child_get_env(tmpenv, "SUPATH");
1088cf2b5f3bSDag-Erling Smørgrav 	else
1089cf2b5f3bSDag-Erling Smørgrav 		var = child_get_env(tmpenv, "PATH");
1090cf2b5f3bSDag-Erling Smørgrav 	if (var != NULL)
1091cf2b5f3bSDag-Erling Smørgrav 		child_set_env(env, envsize, "PATH", var);
1092cf2b5f3bSDag-Erling Smørgrav 
1093cf2b5f3bSDag-Erling Smørgrav 	if ((var = child_get_env(tmpenv, "UMASK")) != NULL)
1094cf2b5f3bSDag-Erling Smørgrav 		if (sscanf(var, "%5lo", &mask) == 1)
10951ec0d754SDag-Erling Smørgrav 			umask((mode_t)mask);
1096cf2b5f3bSDag-Erling Smørgrav 
1097cf2b5f3bSDag-Erling Smørgrav 	for (i = 0; tmpenv[i] != NULL; i++)
1098cf2b5f3bSDag-Erling Smørgrav 		xfree(tmpenv[i]);
1099cf2b5f3bSDag-Erling Smørgrav 	xfree(tmpenv);
1100cf2b5f3bSDag-Erling Smørgrav }
1101cf2b5f3bSDag-Erling Smørgrav #endif /* HAVE_ETC_DEFAULT_LOGIN */
1102cf2b5f3bSDag-Erling Smørgrav 
1103d4ecd108SDag-Erling Smørgrav void
1104d4ecd108SDag-Erling Smørgrav copy_environment(char **source, char ***env, u_int *envsize)
110509958426SBrian Feldman {
1106989dd127SDag-Erling Smørgrav 	char *var_name, *var_val;
110709958426SBrian Feldman 	int i;
110809958426SBrian Feldman 
1109989dd127SDag-Erling Smørgrav 	if (source == NULL)
111009958426SBrian Feldman 		return;
111109958426SBrian Feldman 
1112989dd127SDag-Erling Smørgrav 	for(i = 0; source[i] != NULL; i++) {
1113989dd127SDag-Erling Smørgrav 		var_name = xstrdup(source[i]);
1114989dd127SDag-Erling Smørgrav 		if ((var_val = strstr(var_name, "=")) == NULL) {
1115989dd127SDag-Erling Smørgrav 			xfree(var_name);
111609958426SBrian Feldman 			continue;
1117989dd127SDag-Erling Smørgrav 		}
1118989dd127SDag-Erling Smørgrav 		*var_val++ = '\0';
111909958426SBrian Feldman 
1120989dd127SDag-Erling Smørgrav 		debug3("Copy environment: %s=%s", var_name, var_val);
112109958426SBrian Feldman 		child_set_env(env, envsize, var_name, var_val);
1122989dd127SDag-Erling Smørgrav 
1123989dd127SDag-Erling Smørgrav 		xfree(var_name);
112409958426SBrian Feldman 	}
112509958426SBrian Feldman }
112609958426SBrian Feldman 
1127af12a3e7SDag-Erling Smørgrav static char **
1128989dd127SDag-Erling Smørgrav do_setup_env(Session *s, const char *shell)
1129a04a10f8SKris Kennaway {
1130a04a10f8SKris Kennaway 	char buf[256];
1131af12a3e7SDag-Erling Smørgrav 	u_int i, envsize;
1132333ee039SDag-Erling Smørgrav 	char **env, *laddr;
1133333ee039SDag-Erling Smørgrav 	struct passwd *pw = s->pw;
11347aee6ffeSDag-Erling Smørgrav #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
1135333ee039SDag-Erling Smørgrav 	char *path = NULL;
1136333ee039SDag-Erling Smørgrav #else
1137c62005fcSDag-Erling Smørgrav 	extern char **environ;
1138c62005fcSDag-Erling Smørgrav 	char **senv, **var;
1139c62005fcSDag-Erling Smørgrav #endif
1140a04a10f8SKris Kennaway 
1141a04a10f8SKris Kennaway 	/* Initialize the environment. */
1142a04a10f8SKris Kennaway 	envsize = 100;
1143333ee039SDag-Erling Smørgrav 	env = xcalloc(envsize, sizeof(char *));
1144a04a10f8SKris Kennaway 	env[0] = NULL;
1145989dd127SDag-Erling Smørgrav 
1146989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
1147989dd127SDag-Erling Smørgrav 	/*
1148989dd127SDag-Erling Smørgrav 	 * The Windows environment contains some setting which are
1149989dd127SDag-Erling Smørgrav 	 * important for a running system. They must not be dropped.
1150989dd127SDag-Erling Smørgrav 	 */
1151aa49c926SDag-Erling Smørgrav 	{
1152aa49c926SDag-Erling Smørgrav 		char **p;
1153aa49c926SDag-Erling Smørgrav 
1154aa49c926SDag-Erling Smørgrav 		p = fetch_windows_environment();
1155aa49c926SDag-Erling Smørgrav 		copy_environment(p, &env, &envsize);
1156aa49c926SDag-Erling Smørgrav 		free_windows_environment(p);
1157aa49c926SDag-Erling Smørgrav 	}
1158989dd127SDag-Erling Smørgrav #endif
1159a04a10f8SKris Kennaway 
1160c62005fcSDag-Erling Smørgrav 	if (getenv("TZ"))
1161c62005fcSDag-Erling Smørgrav 		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
1162cf2b5f3bSDag-Erling Smørgrav 
1163cf2b5f3bSDag-Erling Smørgrav #ifdef GSSAPI
1164cf2b5f3bSDag-Erling Smørgrav 	/* Allow any GSSAPI methods that we've used to alter
1165cf2b5f3bSDag-Erling Smørgrav 	 * the childs environment as they see fit
1166cf2b5f3bSDag-Erling Smørgrav 	 */
1167cf2b5f3bSDag-Erling Smørgrav 	ssh_gssapi_do_child(&env, &envsize);
1168cf2b5f3bSDag-Erling Smørgrav #endif
1169cf2b5f3bSDag-Erling Smørgrav 
1170a04a10f8SKris Kennaway 	if (!options.use_login) {
1171a04a10f8SKris Kennaway 		/* Set basic environment. */
117221e764dfSDag-Erling Smørgrav 		for (i = 0; i < s->num_env; i++)
117321e764dfSDag-Erling Smørgrav 			child_set_env(&env, &envsize, s->env[i].name,
117421e764dfSDag-Erling Smørgrav 			    s->env[i].val);
117521e764dfSDag-Erling Smørgrav 
1176a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "USER", pw->pw_name);
1177a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
1178e73e9afaSDag-Erling Smørgrav #ifdef _AIX
1179e73e9afaSDag-Erling Smørgrav 		child_set_env(&env, &envsize, "LOGIN", pw->pw_name);
1180e73e9afaSDag-Erling Smørgrav #endif
1181a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "HOME", pw->pw_dir);
1182c62005fcSDag-Erling Smørgrav 		snprintf(buf, sizeof buf, "%.200s/%.50s",
1183c62005fcSDag-Erling Smørgrav 			 _PATH_MAILDIR, pw->pw_name);
1184c62005fcSDag-Erling Smørgrav 		child_set_env(&env, &envsize, "MAIL", buf);
1185989dd127SDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
1186c62005fcSDag-Erling Smørgrav 		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
1187c62005fcSDag-Erling Smørgrav 		child_set_env(&env, &envsize, "TERM", "su");
1188c62005fcSDag-Erling Smørgrav 		senv = environ;
1189c62005fcSDag-Erling Smørgrav 		environ = xmalloc(sizeof(char *));
1190c62005fcSDag-Erling Smørgrav 		*environ = NULL;
1191c62005fcSDag-Erling Smørgrav 		(void) setusercontext(lc, pw, pw->pw_uid,
1192c62005fcSDag-Erling Smørgrav 		    LOGIN_SETENV|LOGIN_SETPATH);
1193c62005fcSDag-Erling Smørgrav 		copy_environment(environ, &env, &envsize);
1194c62005fcSDag-Erling Smørgrav 		for (var = environ; *var != NULL; ++var)
1195c62005fcSDag-Erling Smørgrav 			xfree(*var);
1196c62005fcSDag-Erling Smørgrav 		xfree(environ);
1197c62005fcSDag-Erling Smørgrav 		environ = senv;
1198989dd127SDag-Erling Smørgrav #else /* HAVE_LOGIN_CAP */
1199989dd127SDag-Erling Smørgrav # ifndef HAVE_CYGWIN
1200989dd127SDag-Erling Smørgrav 		/*
1201989dd127SDag-Erling Smørgrav 		 * There's no standard path on Windows. The path contains
1202989dd127SDag-Erling Smørgrav 		 * important components pointing to the system directories,
1203989dd127SDag-Erling Smørgrav 		 * needed for loading shared libraries. So the path better
1204989dd127SDag-Erling Smørgrav 		 * remains intact here.
1205989dd127SDag-Erling Smørgrav 		 */
1206cf2b5f3bSDag-Erling Smørgrav #  ifdef HAVE_ETC_DEFAULT_LOGIN
1207cf2b5f3bSDag-Erling Smørgrav 		read_etc_default_login(&env, &envsize, pw->pw_uid);
1208cf2b5f3bSDag-Erling Smørgrav 		path = child_get_env(env, "PATH");
1209cf2b5f3bSDag-Erling Smørgrav #  endif /* HAVE_ETC_DEFAULT_LOGIN */
1210cf2b5f3bSDag-Erling Smørgrav 		if (path == NULL || *path == '\0') {
1211989dd127SDag-Erling Smørgrav 			child_set_env(&env, &envsize, "PATH",
1212cf2b5f3bSDag-Erling Smørgrav 			    s->pw->pw_uid == 0 ?
1213cf2b5f3bSDag-Erling Smørgrav 				SUPERUSER_PATH : _PATH_STDPATH);
1214cf2b5f3bSDag-Erling Smørgrav 		}
1215989dd127SDag-Erling Smørgrav # endif /* HAVE_CYGWIN */
1216989dd127SDag-Erling Smørgrav #endif /* HAVE_LOGIN_CAP */
1217a04a10f8SKris Kennaway 
1218a04a10f8SKris Kennaway 		/* Normal systems set SHELL by default. */
1219a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "SHELL", shell);
1220a04a10f8SKris Kennaway 	}
1221a04a10f8SKris Kennaway 
1222a04a10f8SKris Kennaway 	/* Set custom environment options from RSA authentication. */
12231c5093bbSJacques Vidrine 	if (!options.use_login) {
1224a04a10f8SKris Kennaway 		while (custom_environment) {
1225a04a10f8SKris Kennaway 			struct envstring *ce = custom_environment;
1226f388f5efSDag-Erling Smørgrav 			char *str = ce->s;
1227af12a3e7SDag-Erling Smørgrav 
1228f388f5efSDag-Erling Smørgrav 			for (i = 0; str[i] != '=' && str[i]; i++)
1229af12a3e7SDag-Erling Smørgrav 				;
1230f388f5efSDag-Erling Smørgrav 			if (str[i] == '=') {
1231f388f5efSDag-Erling Smørgrav 				str[i] = 0;
1232f388f5efSDag-Erling Smørgrav 				child_set_env(&env, &envsize, str, str + i + 1);
1233a04a10f8SKris Kennaway 			}
1234a04a10f8SKris Kennaway 			custom_environment = ce->next;
1235a04a10f8SKris Kennaway 			xfree(ce->s);
1236a04a10f8SKris Kennaway 			xfree(ce);
1237a04a10f8SKris Kennaway 		}
12381c5093bbSJacques Vidrine 	}
1239a04a10f8SKris Kennaway 
1240f388f5efSDag-Erling Smørgrav 	/* SSH_CLIENT deprecated */
1241a04a10f8SKris Kennaway 	snprintf(buf, sizeof buf, "%.50s %d %d",
1242a04a10f8SKris Kennaway 	    get_remote_ipaddr(), get_remote_port(), get_local_port());
1243a04a10f8SKris Kennaway 	child_set_env(&env, &envsize, "SSH_CLIENT", buf);
1244a04a10f8SKris Kennaway 
1245e73e9afaSDag-Erling Smørgrav 	laddr = get_local_ipaddr(packet_get_connection_in());
1246f388f5efSDag-Erling Smørgrav 	snprintf(buf, sizeof buf, "%.50s %d %.50s %d",
1247e73e9afaSDag-Erling Smørgrav 	    get_remote_ipaddr(), get_remote_port(), laddr, get_local_port());
1248e73e9afaSDag-Erling Smørgrav 	xfree(laddr);
1249f388f5efSDag-Erling Smørgrav 	child_set_env(&env, &envsize, "SSH_CONNECTION", buf);
1250f388f5efSDag-Erling Smørgrav 
1251ca3176e7SBrian Feldman 	if (s->ttyfd != -1)
1252ca3176e7SBrian Feldman 		child_set_env(&env, &envsize, "SSH_TTY", s->tty);
1253ca3176e7SBrian Feldman 	if (s->term)
1254ca3176e7SBrian Feldman 		child_set_env(&env, &envsize, "TERM", s->term);
1255ca3176e7SBrian Feldman 	if (s->display)
1256ca3176e7SBrian Feldman 		child_set_env(&env, &envsize, "DISPLAY", s->display);
1257c2d3a559SKris Kennaway 	if (original_command)
1258c2d3a559SKris Kennaway 		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
1259c2d3a559SKris Kennaway 		    original_command);
1260989dd127SDag-Erling Smørgrav 
1261f388f5efSDag-Erling Smørgrav #ifdef _UNICOS
1262f388f5efSDag-Erling Smørgrav 	if (cray_tmpdir[0] != '\0')
1263f388f5efSDag-Erling Smørgrav 		child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir);
1264f388f5efSDag-Erling Smørgrav #endif /* _UNICOS */
1265f388f5efSDag-Erling Smørgrav 
1266aa49c926SDag-Erling Smørgrav 	/*
1267aa49c926SDag-Erling Smørgrav 	 * Since we clear KRB5CCNAME at startup, if it's set now then it
1268aa49c926SDag-Erling Smørgrav 	 * must have been set by a native authentication method (eg AIX or
1269aa49c926SDag-Erling Smørgrav 	 * SIA), so copy it to the child.
1270aa49c926SDag-Erling Smørgrav 	 */
1271aa49c926SDag-Erling Smørgrav 	{
1272aa49c926SDag-Erling Smørgrav 		char *cp;
1273aa49c926SDag-Erling Smørgrav 
1274aa49c926SDag-Erling Smørgrav 		if ((cp = getenv("KRB5CCNAME")) != NULL)
1275aa49c926SDag-Erling Smørgrav 			child_set_env(&env, &envsize, "KRB5CCNAME", cp);
1276aa49c926SDag-Erling Smørgrav 	}
1277aa49c926SDag-Erling Smørgrav 
1278989dd127SDag-Erling Smørgrav #ifdef _AIX
1279989dd127SDag-Erling Smørgrav 	{
1280989dd127SDag-Erling Smørgrav 		char *cp;
1281989dd127SDag-Erling Smørgrav 
1282989dd127SDag-Erling Smørgrav 		if ((cp = getenv("AUTHSTATE")) != NULL)
1283989dd127SDag-Erling Smørgrav 			child_set_env(&env, &envsize, "AUTHSTATE", cp);
1284989dd127SDag-Erling Smørgrav 		read_environment_file(&env, &envsize, "/etc/environment");
1285989dd127SDag-Erling Smørgrav 	}
1286989dd127SDag-Erling Smørgrav #endif
1287e8aafc91SKris Kennaway #ifdef KRB5
12885962c0e9SDag-Erling Smørgrav 	if (s->authctxt->krb5_ccname)
1289af12a3e7SDag-Erling Smørgrav 		child_set_env(&env, &envsize, "KRB5CCNAME",
12905962c0e9SDag-Erling Smørgrav 		    s->authctxt->krb5_ccname);
1291af12a3e7SDag-Erling Smørgrav #endif
129209958426SBrian Feldman #ifdef USE_PAM
1293f388f5efSDag-Erling Smørgrav 	/*
1294f388f5efSDag-Erling Smørgrav 	 * Pull in any environment variables that may have
1295f388f5efSDag-Erling Smørgrav 	 * been set by PAM.
1296f388f5efSDag-Erling Smørgrav 	 */
1297cf2b5f3bSDag-Erling Smørgrav 	if (options.use_pam) {
12981ec0d754SDag-Erling Smørgrav 		char **p;
1299f388f5efSDag-Erling Smørgrav 
13001ec0d754SDag-Erling Smørgrav 		p = fetch_pam_child_environment();
13011ec0d754SDag-Erling Smørgrav 		copy_environment(p, &env, &envsize);
13021ec0d754SDag-Erling Smørgrav 		free_pam_environment(p);
13031ec0d754SDag-Erling Smørgrav 
13041ec0d754SDag-Erling Smørgrav 		p = fetch_pam_environment();
1305f388f5efSDag-Erling Smørgrav 		copy_environment(p, &env, &envsize);
1306f388f5efSDag-Erling Smørgrav 		free_pam_environment(p);
1307f388f5efSDag-Erling Smørgrav 	}
130809958426SBrian Feldman #endif /* USE_PAM */
130909958426SBrian Feldman 
131080628bacSDag-Erling Smørgrav 	if (auth_sock_name != NULL)
1311a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
131280628bacSDag-Erling Smørgrav 		    auth_sock_name);
1313a04a10f8SKris Kennaway 
1314a04a10f8SKris Kennaway 	/* read $HOME/.ssh/environment. */
1315f388f5efSDag-Erling Smørgrav 	if (options.permit_user_env && !options.use_login) {
1316db1cb46cSKris Kennaway 		snprintf(buf, sizeof buf, "%.200s/.ssh/environment",
1317f388f5efSDag-Erling Smørgrav 		    strcmp(pw->pw_dir, "/") ? pw->pw_dir : "");
1318a04a10f8SKris Kennaway 		read_environment_file(&env, &envsize, buf);
1319a04a10f8SKris Kennaway 	}
1320a04a10f8SKris Kennaway 	if (debug_flag) {
1321a04a10f8SKris Kennaway 		/* dump the environment */
1322a04a10f8SKris Kennaway 		fprintf(stderr, "Environment:\n");
1323a04a10f8SKris Kennaway 		for (i = 0; env[i]; i++)
1324a04a10f8SKris Kennaway 			fprintf(stderr, "  %.200s\n", env[i]);
1325a04a10f8SKris Kennaway 	}
1326af12a3e7SDag-Erling Smørgrav 	return env;
1327e8aafc91SKris Kennaway }
1328ca3176e7SBrian Feldman 
1329ca3176e7SBrian Feldman /*
1330af12a3e7SDag-Erling Smørgrav  * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found
1331af12a3e7SDag-Erling Smørgrav  * first in this order).
1332ca3176e7SBrian Feldman  */
1333af12a3e7SDag-Erling Smørgrav static void
1334af12a3e7SDag-Erling Smørgrav do_rc_files(Session *s, const char *shell)
1335af12a3e7SDag-Erling Smørgrav {
1336af12a3e7SDag-Erling Smørgrav 	FILE *f = NULL;
1337af12a3e7SDag-Erling Smørgrav 	char cmd[1024];
1338af12a3e7SDag-Erling Smørgrav 	int do_xauth;
1339af12a3e7SDag-Erling Smørgrav 	struct stat st;
1340a04a10f8SKris Kennaway 
1341af12a3e7SDag-Erling Smørgrav 	do_xauth =
1342af12a3e7SDag-Erling Smørgrav 	    s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
1343a04a10f8SKris Kennaway 
1344d4af9e69SDag-Erling Smørgrav 	/* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
1345d4af9e69SDag-Erling Smørgrav 	if (!s->is_subsystem && options.adm_forced_command == NULL &&
1346d4af9e69SDag-Erling Smørgrav 	    !no_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) {
1347e9fd63dfSBrian Feldman 		snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
1348e9fd63dfSBrian Feldman 		    shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
1349a04a10f8SKris Kennaway 		if (debug_flag)
1350e9fd63dfSBrian Feldman 			fprintf(stderr, "Running %s\n", cmd);
1351e9fd63dfSBrian Feldman 		f = popen(cmd, "w");
1352a04a10f8SKris Kennaway 		if (f) {
1353ca3176e7SBrian Feldman 			if (do_xauth)
1354ca3176e7SBrian Feldman 				fprintf(f, "%s %s\n", s->auth_proto,
1355ca3176e7SBrian Feldman 				    s->auth_data);
1356a04a10f8SKris Kennaway 			pclose(f);
1357a04a10f8SKris Kennaway 		} else
1358ca3176e7SBrian Feldman 			fprintf(stderr, "Could not run %s\n",
1359ca3176e7SBrian Feldman 			    _PATH_SSH_USER_RC);
1360ca3176e7SBrian Feldman 	} else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
1361a04a10f8SKris Kennaway 		if (debug_flag)
1362ca3176e7SBrian Feldman 			fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
1363ca3176e7SBrian Feldman 			    _PATH_SSH_SYSTEM_RC);
1364ca3176e7SBrian Feldman 		f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
1365a04a10f8SKris Kennaway 		if (f) {
1366ca3176e7SBrian Feldman 			if (do_xauth)
1367ca3176e7SBrian Feldman 				fprintf(f, "%s %s\n", s->auth_proto,
1368ca3176e7SBrian Feldman 				    s->auth_data);
1369a04a10f8SKris Kennaway 			pclose(f);
1370a04a10f8SKris Kennaway 		} else
1371ca3176e7SBrian Feldman 			fprintf(stderr, "Could not run %s\n",
1372ca3176e7SBrian Feldman 			    _PATH_SSH_SYSTEM_RC);
1373ca3176e7SBrian Feldman 	} else if (do_xauth && options.xauth_location != NULL) {
1374a04a10f8SKris Kennaway 		/* Add authority data to .Xauthority if appropriate. */
1375db1cb46cSKris Kennaway 		if (debug_flag) {
1376db1cb46cSKris Kennaway 			fprintf(stderr,
1377e73e9afaSDag-Erling Smørgrav 			    "Running %.500s remove %.100s\n",
1378e73e9afaSDag-Erling Smørgrav 			    options.xauth_location, s->auth_display);
1379e73e9afaSDag-Erling Smørgrav 			fprintf(stderr,
1380e73e9afaSDag-Erling Smørgrav 			    "%.500s add %.100s %.100s %.100s\n",
1381af12a3e7SDag-Erling Smørgrav 			    options.xauth_location, s->auth_display,
1382ca3176e7SBrian Feldman 			    s->auth_proto, s->auth_data);
1383db1cb46cSKris Kennaway 		}
1384c2d3a559SKris Kennaway 		snprintf(cmd, sizeof cmd, "%s -q -",
1385c2d3a559SKris Kennaway 		    options.xauth_location);
1386c2d3a559SKris Kennaway 		f = popen(cmd, "w");
1387a04a10f8SKris Kennaway 		if (f) {
1388e73e9afaSDag-Erling Smørgrav 			fprintf(f, "remove %s\n",
1389e73e9afaSDag-Erling Smørgrav 			    s->auth_display);
1390af12a3e7SDag-Erling Smørgrav 			fprintf(f, "add %s %s %s\n",
1391af12a3e7SDag-Erling Smørgrav 			    s->auth_display, s->auth_proto,
1392ca3176e7SBrian Feldman 			    s->auth_data);
1393a04a10f8SKris Kennaway 			pclose(f);
1394c2d3a559SKris Kennaway 		} else {
1395c2d3a559SKris Kennaway 			fprintf(stderr, "Could not run %s\n",
1396c2d3a559SKris Kennaway 			    cmd);
1397a04a10f8SKris Kennaway 		}
1398a04a10f8SKris Kennaway 	}
1399a04a10f8SKris Kennaway }
1400ca3176e7SBrian Feldman 
1401af12a3e7SDag-Erling Smørgrav static void
1402af12a3e7SDag-Erling Smørgrav do_nologin(struct passwd *pw)
1403af12a3e7SDag-Erling Smørgrav {
1404af12a3e7SDag-Erling Smørgrav 	FILE *f = NULL;
1405b15c8340SDag-Erling Smørgrav 	char buf[1024], *nl, *def_nl = _PATH_NOLOGIN;
1406b15c8340SDag-Erling Smørgrav 	struct stat sb;
1407af12a3e7SDag-Erling Smørgrav 
1408af12a3e7SDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
1409462c32cbSDag-Erling Smørgrav 	if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0)
1410b15c8340SDag-Erling Smørgrav 		return;
1411b15c8340SDag-Erling Smørgrav 	nl = login_getcapstr(lc, "nologin", def_nl, def_nl);
1412af12a3e7SDag-Erling Smørgrav #else
1413b15c8340SDag-Erling Smørgrav 	if (pw->pw_uid == 0)
1414b15c8340SDag-Erling Smørgrav 		return;
1415b15c8340SDag-Erling Smørgrav 	nl = def_nl;
1416af12a3e7SDag-Erling Smørgrav #endif
1417b15c8340SDag-Erling Smørgrav 	if (stat(nl, &sb) == -1) {
1418b15c8340SDag-Erling Smørgrav 		if (nl != def_nl)
1419b15c8340SDag-Erling Smørgrav 			xfree(nl);
1420b15c8340SDag-Erling Smørgrav 		return;
1421b15c8340SDag-Erling Smørgrav 	}
1422b15c8340SDag-Erling Smørgrav 
1423b15c8340SDag-Erling Smørgrav 	/* /etc/nologin exists.  Print its contents if we can and exit. */
1424b15c8340SDag-Erling Smørgrav 	logit("User %.100s not allowed because %s exists", pw->pw_name, nl);
1425b15c8340SDag-Erling Smørgrav 	if ((f = fopen(nl, "r")) != NULL) {
1426af12a3e7SDag-Erling Smørgrav  		while (fgets(buf, sizeof(buf), f))
1427af12a3e7SDag-Erling Smørgrav  			fputs(buf, stderr);
1428af12a3e7SDag-Erling Smørgrav  		fclose(f);
1429af12a3e7SDag-Erling Smørgrav  	}
1430b15c8340SDag-Erling Smørgrav 	exit(254);
1431af12a3e7SDag-Erling Smørgrav }
1432af12a3e7SDag-Erling Smørgrav 
1433d4af9e69SDag-Erling Smørgrav /*
1434d4af9e69SDag-Erling Smørgrav  * Chroot into a directory after checking it for safety: all path components
1435d4af9e69SDag-Erling Smørgrav  * must be root-owned directories with strict permissions.
1436d4af9e69SDag-Erling Smørgrav  */
1437d4af9e69SDag-Erling Smørgrav static void
1438d4af9e69SDag-Erling Smørgrav safely_chroot(const char *path, uid_t uid)
1439d4af9e69SDag-Erling Smørgrav {
1440d4af9e69SDag-Erling Smørgrav 	const char *cp;
1441d4af9e69SDag-Erling Smørgrav 	char component[MAXPATHLEN];
1442d4af9e69SDag-Erling Smørgrav 	struct stat st;
1443d4af9e69SDag-Erling Smørgrav 
1444d4af9e69SDag-Erling Smørgrav 	if (*path != '/')
1445d4af9e69SDag-Erling Smørgrav 		fatal("chroot path does not begin at root");
1446d4af9e69SDag-Erling Smørgrav 	if (strlen(path) >= sizeof(component))
1447d4af9e69SDag-Erling Smørgrav 		fatal("chroot path too long");
1448d4af9e69SDag-Erling Smørgrav 
1449d4af9e69SDag-Erling Smørgrav 	/*
1450d4af9e69SDag-Erling Smørgrav 	 * Descend the path, checking that each component is a
1451d4af9e69SDag-Erling Smørgrav 	 * root-owned directory with strict permissions.
1452d4af9e69SDag-Erling Smørgrav 	 */
1453d4af9e69SDag-Erling Smørgrav 	for (cp = path; cp != NULL;) {
1454d4af9e69SDag-Erling Smørgrav 		if ((cp = strchr(cp, '/')) == NULL)
1455d4af9e69SDag-Erling Smørgrav 			strlcpy(component, path, sizeof(component));
1456d4af9e69SDag-Erling Smørgrav 		else {
1457d4af9e69SDag-Erling Smørgrav 			cp++;
1458d4af9e69SDag-Erling Smørgrav 			memcpy(component, path, cp - path);
1459d4af9e69SDag-Erling Smørgrav 			component[cp - path] = '\0';
1460d4af9e69SDag-Erling Smørgrav 		}
1461d4af9e69SDag-Erling Smørgrav 
1462d4af9e69SDag-Erling Smørgrav 		debug3("%s: checking '%s'", __func__, component);
1463d4af9e69SDag-Erling Smørgrav 
1464d4af9e69SDag-Erling Smørgrav 		if (stat(component, &st) != 0)
1465d4af9e69SDag-Erling Smørgrav 			fatal("%s: stat(\"%s\"): %s", __func__,
1466d4af9e69SDag-Erling Smørgrav 			    component, strerror(errno));
1467d4af9e69SDag-Erling Smørgrav 		if (st.st_uid != 0 || (st.st_mode & 022) != 0)
1468d4af9e69SDag-Erling Smørgrav 			fatal("bad ownership or modes for chroot "
1469d4af9e69SDag-Erling Smørgrav 			    "directory %s\"%s\"",
1470d4af9e69SDag-Erling Smørgrav 			    cp == NULL ? "" : "component ", component);
1471d4af9e69SDag-Erling Smørgrav 		if (!S_ISDIR(st.st_mode))
1472d4af9e69SDag-Erling Smørgrav 			fatal("chroot path %s\"%s\" is not a directory",
1473d4af9e69SDag-Erling Smørgrav 			    cp == NULL ? "" : "component ", component);
1474d4af9e69SDag-Erling Smørgrav 
1475d4af9e69SDag-Erling Smørgrav 	}
1476d4af9e69SDag-Erling Smørgrav 
1477d4af9e69SDag-Erling Smørgrav 	if (chdir(path) == -1)
1478d4af9e69SDag-Erling Smørgrav 		fatal("Unable to chdir to chroot path \"%s\": "
1479d4af9e69SDag-Erling Smørgrav 		    "%s", path, strerror(errno));
1480d4af9e69SDag-Erling Smørgrav 	if (chroot(path) == -1)
1481d4af9e69SDag-Erling Smørgrav 		fatal("chroot(\"%s\"): %s", path, strerror(errno));
1482d4af9e69SDag-Erling Smørgrav 	if (chdir("/") == -1)
1483d4af9e69SDag-Erling Smørgrav 		fatal("%s: chdir(/) after chroot: %s",
1484d4af9e69SDag-Erling Smørgrav 		    __func__, strerror(errno));
1485d4af9e69SDag-Erling Smørgrav 	verbose("Changed root directory to \"%s\"", path);
1486d4af9e69SDag-Erling Smørgrav }
1487d4af9e69SDag-Erling Smørgrav 
1488af12a3e7SDag-Erling Smørgrav /* Set login name, uid, gid, and groups. */
1489989dd127SDag-Erling Smørgrav void
149080628bacSDag-Erling Smørgrav do_setusercontext(struct passwd *pw)
1491af12a3e7SDag-Erling Smørgrav {
1492d4af9e69SDag-Erling Smørgrav 	char *chroot_path, *tmp;
1493d4af9e69SDag-Erling Smørgrav 
14944a421b63SDag-Erling Smørgrav 	platform_setusercontext(pw);
1495d4af9e69SDag-Erling Smørgrav 
14964a421b63SDag-Erling Smørgrav 	if (platform_privileged_uidswap()) {
1497989dd127SDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
1498989dd127SDag-Erling Smørgrav 		if (setusercontext(lc, pw, pw->pw_uid,
1499d4af9e69SDag-Erling Smørgrav 		    (LOGIN_SETALL & ~(LOGIN_SETENV|LOGIN_SETPATH|LOGIN_SETUSER))) < 0) {
1500989dd127SDag-Erling Smørgrav 			perror("unable to set user context");
1501989dd127SDag-Erling Smørgrav 			exit(1);
1502989dd127SDag-Erling Smørgrav 		}
1503989dd127SDag-Erling Smørgrav #else
1504af12a3e7SDag-Erling Smørgrav 		if (setlogin(pw->pw_name) < 0)
1505af12a3e7SDag-Erling Smørgrav 			error("setlogin failed: %s", strerror(errno));
1506af12a3e7SDag-Erling Smørgrav 		if (setgid(pw->pw_gid) < 0) {
1507af12a3e7SDag-Erling Smørgrav 			perror("setgid");
1508af12a3e7SDag-Erling Smørgrav 			exit(1);
1509af12a3e7SDag-Erling Smørgrav 		}
1510af12a3e7SDag-Erling Smørgrav 		/* Initialize the group list. */
1511af12a3e7SDag-Erling Smørgrav 		if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
1512af12a3e7SDag-Erling Smørgrav 			perror("initgroups");
1513af12a3e7SDag-Erling Smørgrav 			exit(1);
1514af12a3e7SDag-Erling Smørgrav 		}
1515af12a3e7SDag-Erling Smørgrav 		endgrent();
1516d4af9e69SDag-Erling Smørgrav #endif
1517b15c8340SDag-Erling Smørgrav 
15184a421b63SDag-Erling Smørgrav 		platform_setusercontext_post_groups(pw);
15198ad9b54aSDag-Erling Smørgrav 
1520d4af9e69SDag-Erling Smørgrav 		if (options.chroot_directory != NULL &&
1521d4af9e69SDag-Erling Smørgrav 		    strcasecmp(options.chroot_directory, "none") != 0) {
1522d4af9e69SDag-Erling Smørgrav                         tmp = tilde_expand_filename(options.chroot_directory,
1523d4af9e69SDag-Erling Smørgrav 			    pw->pw_uid);
1524d4af9e69SDag-Erling Smørgrav 			chroot_path = percent_expand(tmp, "h", pw->pw_dir,
1525d4af9e69SDag-Erling Smørgrav 			    "u", pw->pw_name, (char *)NULL);
1526d4af9e69SDag-Erling Smørgrav 			safely_chroot(chroot_path, pw->pw_uid);
1527d4af9e69SDag-Erling Smørgrav 			free(tmp);
1528d4af9e69SDag-Erling Smørgrav 			free(chroot_path);
1529d4af9e69SDag-Erling Smørgrav 		}
1530d4af9e69SDag-Erling Smørgrav 
1531d4af9e69SDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
1532d4af9e69SDag-Erling Smørgrav 		if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) {
1533d4af9e69SDag-Erling Smørgrav 			perror("unable to set user context (setuser)");
1534d4af9e69SDag-Erling Smørgrav 			exit(1);
1535d4af9e69SDag-Erling Smørgrav 		}
1536*2ec88e9dSDag-Erling Smørgrav 
1537*2ec88e9dSDag-Erling Smørgrav 		/*
1538*2ec88e9dSDag-Erling Smørgrav 		 * FreeBSD's setusercontext() will not apply the user's
1539*2ec88e9dSDag-Erling Smørgrav 		 * own umask setting unless running with the user's UID.
1540*2ec88e9dSDag-Erling Smørgrav 		 */
1541*2ec88e9dSDag-Erling Smørgrav 		setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK);
1542d4af9e69SDag-Erling Smørgrav #else
1543af12a3e7SDag-Erling Smørgrav 		/* Permanently switch to the desired uid. */
1544af12a3e7SDag-Erling Smørgrav 		permanently_set_uid(pw);
1545989dd127SDag-Erling Smørgrav #endif
1546af12a3e7SDag-Erling Smørgrav 	}
1547e73e9afaSDag-Erling Smørgrav 
1548af12a3e7SDag-Erling Smørgrav 	if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
1549af12a3e7SDag-Erling Smørgrav 		fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
155080628bacSDag-Erling Smørgrav }
155180628bacSDag-Erling Smørgrav 
155280628bacSDag-Erling Smørgrav static void
15531ec0d754SDag-Erling Smørgrav do_pwchange(Session *s)
15541ec0d754SDag-Erling Smørgrav {
155521e764dfSDag-Erling Smørgrav 	fflush(NULL);
15561ec0d754SDag-Erling Smørgrav 	fprintf(stderr, "WARNING: Your password has expired.\n");
15571ec0d754SDag-Erling Smørgrav 	if (s->ttyfd != -1) {
15581ec0d754SDag-Erling Smørgrav 		fprintf(stderr,
15591ec0d754SDag-Erling Smørgrav 		    "You must change your password now and login again!\n");
1560e146993eSDag-Erling Smørgrav #ifdef WITH_SELINUX
1561e146993eSDag-Erling Smørgrav 		setexeccon(NULL);
1562e146993eSDag-Erling Smørgrav #endif
1563aa49c926SDag-Erling Smørgrav #ifdef PASSWD_NEEDS_USERNAME
1564aa49c926SDag-Erling Smørgrav 		execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name,
1565aa49c926SDag-Erling Smørgrav 		    (char *)NULL);
1566aa49c926SDag-Erling Smørgrav #else
15671ec0d754SDag-Erling Smørgrav 		execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL);
1568aa49c926SDag-Erling Smørgrav #endif
15691ec0d754SDag-Erling Smørgrav 		perror("passwd");
15701ec0d754SDag-Erling Smørgrav 	} else {
15711ec0d754SDag-Erling Smørgrav 		fprintf(stderr,
15721ec0d754SDag-Erling Smørgrav 		    "Password change required but no TTY available.\n");
15731ec0d754SDag-Erling Smørgrav 	}
15741ec0d754SDag-Erling Smørgrav 	exit(1);
15751ec0d754SDag-Erling Smørgrav }
15761ec0d754SDag-Erling Smørgrav 
15771ec0d754SDag-Erling Smørgrav static void
157880628bacSDag-Erling Smørgrav launch_login(struct passwd *pw, const char *hostname)
157980628bacSDag-Erling Smørgrav {
158080628bacSDag-Erling Smørgrav 	/* Launch login(1). */
158180628bacSDag-Erling Smørgrav 
1582989dd127SDag-Erling Smørgrav 	execl(LOGIN_PROGRAM, "login", "-h", hostname,
1583989dd127SDag-Erling Smørgrav #ifdef xxxLOGIN_NEEDS_TERM
1584989dd127SDag-Erling Smørgrav 		    (s->term ? s->term : "unknown"),
1585989dd127SDag-Erling Smørgrav #endif /* LOGIN_NEEDS_TERM */
1586989dd127SDag-Erling Smørgrav #ifdef LOGIN_NO_ENDOPT
1587989dd127SDag-Erling Smørgrav 	    "-p", "-f", pw->pw_name, (char *)NULL);
1588989dd127SDag-Erling Smørgrav #else
158980628bacSDag-Erling Smørgrav 	    "-p", "-f", "--", pw->pw_name, (char *)NULL);
1590989dd127SDag-Erling Smørgrav #endif
159180628bacSDag-Erling Smørgrav 
159280628bacSDag-Erling Smørgrav 	/* Login couldn't be executed, die. */
159380628bacSDag-Erling Smørgrav 
159480628bacSDag-Erling Smørgrav 	perror("login");
159580628bacSDag-Erling Smørgrav 	exit(1);
1596af12a3e7SDag-Erling Smørgrav }
1597af12a3e7SDag-Erling Smørgrav 
15981ec0d754SDag-Erling Smørgrav static void
15991ec0d754SDag-Erling Smørgrav child_close_fds(void)
16001ec0d754SDag-Erling Smørgrav {
16011ec0d754SDag-Erling Smørgrav 	if (packet_get_connection_in() == packet_get_connection_out())
16021ec0d754SDag-Erling Smørgrav 		close(packet_get_connection_in());
16031ec0d754SDag-Erling Smørgrav 	else {
16041ec0d754SDag-Erling Smørgrav 		close(packet_get_connection_in());
16051ec0d754SDag-Erling Smørgrav 		close(packet_get_connection_out());
16061ec0d754SDag-Erling Smørgrav 	}
16071ec0d754SDag-Erling Smørgrav 	/*
16081ec0d754SDag-Erling Smørgrav 	 * Close all descriptors related to channels.  They will still remain
16091ec0d754SDag-Erling Smørgrav 	 * open in the parent.
16101ec0d754SDag-Erling Smørgrav 	 */
16111ec0d754SDag-Erling Smørgrav 	/* XXX better use close-on-exec? -markus */
16121ec0d754SDag-Erling Smørgrav 	channel_close_all();
16131ec0d754SDag-Erling Smørgrav 
16141ec0d754SDag-Erling Smørgrav 	/*
16151ec0d754SDag-Erling Smørgrav 	 * Close any extra file descriptors.  Note that there may still be
16161ec0d754SDag-Erling Smørgrav 	 * descriptors left by system functions.  They will be closed later.
16171ec0d754SDag-Erling Smørgrav 	 */
16181ec0d754SDag-Erling Smørgrav 	endpwent();
16191ec0d754SDag-Erling Smørgrav 
16201ec0d754SDag-Erling Smørgrav 	/*
1621b74df5b2SDag-Erling Smørgrav 	 * Close any extra open file descriptors so that we don't have them
16221ec0d754SDag-Erling Smørgrav 	 * hanging around in clients.  Note that we want to do this after
16231ec0d754SDag-Erling Smørgrav 	 * initgroups, because at least on Solaris 2.3 it leaves file
16241ec0d754SDag-Erling Smørgrav 	 * descriptors open.
16251ec0d754SDag-Erling Smørgrav 	 */
16264a421b63SDag-Erling Smørgrav 	closefrom(STDERR_FILENO + 1);
16271ec0d754SDag-Erling Smørgrav }
16281ec0d754SDag-Erling Smørgrav 
1629af12a3e7SDag-Erling Smørgrav /*
1630af12a3e7SDag-Erling Smørgrav  * Performs common processing for the child, such as setting up the
1631af12a3e7SDag-Erling Smørgrav  * environment, closing extra file descriptors, setting the user and group
1632af12a3e7SDag-Erling Smørgrav  * ids, and executing the command or shell.
1633af12a3e7SDag-Erling Smørgrav  */
1634d4af9e69SDag-Erling Smørgrav #define ARGV_MAX 10
1635af12a3e7SDag-Erling Smørgrav void
1636af12a3e7SDag-Erling Smørgrav do_child(Session *s, const char *command)
1637af12a3e7SDag-Erling Smørgrav {
1638af12a3e7SDag-Erling Smørgrav 	extern char **environ;
1639989dd127SDag-Erling Smørgrav 	char **env;
1640d4af9e69SDag-Erling Smørgrav 	char *argv[ARGV_MAX];
1641af12a3e7SDag-Erling Smørgrav 	const char *shell, *shell0, *hostname = NULL;
1642af12a3e7SDag-Erling Smørgrav 	struct passwd *pw = s->pw;
1643d4af9e69SDag-Erling Smørgrav 	int r = 0;
1644af12a3e7SDag-Erling Smørgrav 
1645af12a3e7SDag-Erling Smørgrav 	/* remove hostkey from the child's memory */
1646af12a3e7SDag-Erling Smørgrav 	destroy_sensitive_data();
1647af12a3e7SDag-Erling Smørgrav 
16481ec0d754SDag-Erling Smørgrav 	/* Force a password change */
16491ec0d754SDag-Erling Smørgrav 	if (s->authctxt->force_pwchange) {
16501ec0d754SDag-Erling Smørgrav 		do_setusercontext(pw);
16511ec0d754SDag-Erling Smørgrav 		child_close_fds();
16521ec0d754SDag-Erling Smørgrav 		do_pwchange(s);
16531ec0d754SDag-Erling Smørgrav 		exit(1);
16541ec0d754SDag-Erling Smørgrav 	}
16551ec0d754SDag-Erling Smørgrav 
1656af12a3e7SDag-Erling Smørgrav 	/* login(1) is only called if we execute the login shell */
1657af12a3e7SDag-Erling Smørgrav 	if (options.use_login && command != NULL)
1658af12a3e7SDag-Erling Smørgrav 		options.use_login = 0;
1659af12a3e7SDag-Erling Smørgrav 
1660f388f5efSDag-Erling Smørgrav #ifdef _UNICOS
1661f388f5efSDag-Erling Smørgrav 	cray_setup(pw->pw_uid, pw->pw_name, command);
1662f388f5efSDag-Erling Smørgrav #endif /* _UNICOS */
1663f388f5efSDag-Erling Smørgrav 
1664af12a3e7SDag-Erling Smørgrav 	/*
1665af12a3e7SDag-Erling Smørgrav 	 * Login(1) does this as well, and it needs uid 0 for the "-h"
1666af12a3e7SDag-Erling Smørgrav 	 * switch, so we let login(1) to this for us.
1667af12a3e7SDag-Erling Smørgrav 	 */
1668af12a3e7SDag-Erling Smørgrav 	if (!options.use_login) {
1669989dd127SDag-Erling Smørgrav #ifdef HAVE_OSF_SIA
1670e73e9afaSDag-Erling Smørgrav 		session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty);
1671989dd127SDag-Erling Smørgrav 		if (!check_quietlogin(s, command))
1672989dd127SDag-Erling Smørgrav 			do_motd();
1673989dd127SDag-Erling Smørgrav #else /* HAVE_OSF_SIA */
1674b74df5b2SDag-Erling Smørgrav 		/* When PAM is enabled we rely on it to do the nologin check */
1675b74df5b2SDag-Erling Smørgrav 		if (!options.use_pam)
1676af12a3e7SDag-Erling Smørgrav 			do_nologin(pw);
1677989dd127SDag-Erling Smørgrav 		do_setusercontext(pw);
167821e764dfSDag-Erling Smørgrav 		/*
167921e764dfSDag-Erling Smørgrav 		 * PAM session modules in do_setusercontext may have
168021e764dfSDag-Erling Smørgrav 		 * generated messages, so if this in an interactive
168121e764dfSDag-Erling Smørgrav 		 * login then display them too.
168221e764dfSDag-Erling Smørgrav 		 */
1683aa49c926SDag-Erling Smørgrav 		if (!check_quietlogin(s, command))
168421e764dfSDag-Erling Smørgrav 			display_loginmsg();
1685989dd127SDag-Erling Smørgrav #endif /* HAVE_OSF_SIA */
1686af12a3e7SDag-Erling Smørgrav 	}
1687af12a3e7SDag-Erling Smørgrav 
1688aa49c926SDag-Erling Smørgrav #ifdef USE_PAM
1689aa49c926SDag-Erling Smørgrav 	if (options.use_pam && !options.use_login && !is_pam_session_open()) {
1690aa49c926SDag-Erling Smørgrav 		debug3("PAM session not opened, exiting");
1691aa49c926SDag-Erling Smørgrav 		display_loginmsg();
1692aa49c926SDag-Erling Smørgrav 		exit(254);
1693aa49c926SDag-Erling Smørgrav 	}
1694aa49c926SDag-Erling Smørgrav #endif
1695aa49c926SDag-Erling Smørgrav 
1696af12a3e7SDag-Erling Smørgrav 	/*
1697af12a3e7SDag-Erling Smørgrav 	 * Get the shell from the password data.  An empty shell field is
1698af12a3e7SDag-Erling Smørgrav 	 * legal, and means /bin/sh.
1699af12a3e7SDag-Erling Smørgrav 	 */
1700af12a3e7SDag-Erling Smørgrav 	shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
1701e73e9afaSDag-Erling Smørgrav 
1702e73e9afaSDag-Erling Smørgrav 	/*
1703e73e9afaSDag-Erling Smørgrav 	 * Make sure $SHELL points to the shell from the password file,
1704e73e9afaSDag-Erling Smørgrav 	 * even if shell is overridden from login.conf
1705e73e9afaSDag-Erling Smørgrav 	 */
1706e73e9afaSDag-Erling Smørgrav 	env = do_setup_env(s, shell);
1707e73e9afaSDag-Erling Smørgrav 
1708af12a3e7SDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
1709af12a3e7SDag-Erling Smørgrav 	shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
1710af12a3e7SDag-Erling Smørgrav #endif
1711af12a3e7SDag-Erling Smørgrav 
1712af12a3e7SDag-Erling Smørgrav 	/* we have to stash the hostname before we close our socket. */
1713af12a3e7SDag-Erling Smørgrav 	if (options.use_login)
1714af12a3e7SDag-Erling Smørgrav 		hostname = get_remote_name_or_ip(utmp_len,
1715cf2b5f3bSDag-Erling Smørgrav 		    options.use_dns);
1716af12a3e7SDag-Erling Smørgrav 	/*
1717af12a3e7SDag-Erling Smørgrav 	 * Close the connection descriptors; note that this is the child, and
1718af12a3e7SDag-Erling Smørgrav 	 * the server will still have the socket open, and it is important
1719af12a3e7SDag-Erling Smørgrav 	 * that we do not shutdown it.  Note that the descriptors cannot be
1720af12a3e7SDag-Erling Smørgrav 	 * closed before building the environment, as we call
1721af12a3e7SDag-Erling Smørgrav 	 * get_remote_ipaddr there.
1722af12a3e7SDag-Erling Smørgrav 	 */
17231ec0d754SDag-Erling Smørgrav 	child_close_fds();
1724af12a3e7SDag-Erling Smørgrav 
1725af12a3e7SDag-Erling Smørgrav 	/*
1726af12a3e7SDag-Erling Smørgrav 	 * Must take new environment into use so that .ssh/rc,
1727af12a3e7SDag-Erling Smørgrav 	 * /etc/ssh/sshrc and xauth are run in the proper environment.
1728af12a3e7SDag-Erling Smørgrav 	 */
1729af12a3e7SDag-Erling Smørgrav 	environ = env;
1730af12a3e7SDag-Erling Smørgrav 
17311ec0d754SDag-Erling Smørgrav #if defined(KRB5) && defined(USE_AFS)
17321ec0d754SDag-Erling Smørgrav 	/*
17331ec0d754SDag-Erling Smørgrav 	 * At this point, we check to see if AFS is active and if we have
17341ec0d754SDag-Erling Smørgrav 	 * a valid Kerberos 5 TGT. If so, it seems like a good idea to see
17351ec0d754SDag-Erling Smørgrav 	 * if we can (and need to) extend the ticket into an AFS token. If
17361ec0d754SDag-Erling Smørgrav 	 * we don't do this, we run into potential problems if the user's
17371ec0d754SDag-Erling Smørgrav 	 * home directory is in AFS and it's not world-readable.
17381ec0d754SDag-Erling Smørgrav 	 */
17391ec0d754SDag-Erling Smørgrav 
17401ec0d754SDag-Erling Smørgrav 	if (options.kerberos_get_afs_token && k_hasafs() &&
17411ec0d754SDag-Erling Smørgrav 	    (s->authctxt->krb5_ctx != NULL)) {
17421ec0d754SDag-Erling Smørgrav 		char cell[64];
17431ec0d754SDag-Erling Smørgrav 
17441ec0d754SDag-Erling Smørgrav 		debug("Getting AFS token");
17451ec0d754SDag-Erling Smørgrav 
17461ec0d754SDag-Erling Smørgrav 		k_setpag();
17471ec0d754SDag-Erling Smørgrav 
17481ec0d754SDag-Erling Smørgrav 		if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
17491ec0d754SDag-Erling Smørgrav 			krb5_afslog(s->authctxt->krb5_ctx,
17501ec0d754SDag-Erling Smørgrav 			    s->authctxt->krb5_fwd_ccache, cell, NULL);
17511ec0d754SDag-Erling Smørgrav 
17521ec0d754SDag-Erling Smørgrav 		krb5_afslog_home(s->authctxt->krb5_ctx,
17531ec0d754SDag-Erling Smørgrav 		    s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir);
17541ec0d754SDag-Erling Smørgrav 	}
17551ec0d754SDag-Erling Smørgrav #endif
17561ec0d754SDag-Erling Smørgrav 
1757b74df5b2SDag-Erling Smørgrav 	/* Change current directory to the user's home directory. */
1758af12a3e7SDag-Erling Smørgrav 	if (chdir(pw->pw_dir) < 0) {
1759d4af9e69SDag-Erling Smørgrav 		/* Suppress missing homedir warning for chroot case */
1760af12a3e7SDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
1761d4af9e69SDag-Erling Smørgrav 		r = login_getcapbool(lc, "requirehome", 0);
1762af12a3e7SDag-Erling Smørgrav #endif
1763e2f6069cSDag-Erling Smørgrav 		if (r || options.chroot_directory == NULL ||
1764e2f6069cSDag-Erling Smørgrav 		    strcasecmp(options.chroot_directory, "none") == 0)
1765d4af9e69SDag-Erling Smørgrav 			fprintf(stderr, "Could not chdir to home "
1766d4af9e69SDag-Erling Smørgrav 			    "directory %s: %s\n", pw->pw_dir,
1767d4af9e69SDag-Erling Smørgrav 			    strerror(errno));
1768d4af9e69SDag-Erling Smørgrav 		if (r)
1769d4af9e69SDag-Erling Smørgrav 			exit(1);
1770af12a3e7SDag-Erling Smørgrav 	}
1771af12a3e7SDag-Erling Smørgrav 
1772d4af9e69SDag-Erling Smørgrav 	closefrom(STDERR_FILENO + 1);
1773d4af9e69SDag-Erling Smørgrav 
1774af12a3e7SDag-Erling Smørgrav 	if (!options.use_login)
1775af12a3e7SDag-Erling Smørgrav 		do_rc_files(s, shell);
1776af12a3e7SDag-Erling Smørgrav 
1777ca3176e7SBrian Feldman 	/* restore SIGPIPE for child */
1778ca3176e7SBrian Feldman 	signal(SIGPIPE, SIG_DFL);
1779ca3176e7SBrian Feldman 
1780b15c8340SDag-Erling Smørgrav 	if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) {
1781b15c8340SDag-Erling Smørgrav 		printf("This service allows sftp connections only.\n");
1782b15c8340SDag-Erling Smørgrav 		fflush(NULL);
1783b15c8340SDag-Erling Smørgrav 		exit(1);
1784b15c8340SDag-Erling Smørgrav 	} else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
1785d4af9e69SDag-Erling Smørgrav 		extern int optind, optreset;
1786d4af9e69SDag-Erling Smørgrav 		int i;
1787d4af9e69SDag-Erling Smørgrav 		char *p, *args;
1788d4af9e69SDag-Erling Smørgrav 
17897aee6ffeSDag-Erling Smørgrav 		setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME);
1790cce7d346SDag-Erling Smørgrav 		args = xstrdup(command ? command : "sftp-server");
1791d4af9e69SDag-Erling Smørgrav 		for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " ")))
1792d4af9e69SDag-Erling Smørgrav 			if (i < ARGV_MAX - 1)
1793d4af9e69SDag-Erling Smørgrav 				argv[i++] = p;
1794d4af9e69SDag-Erling Smørgrav 		argv[i] = NULL;
1795d4af9e69SDag-Erling Smørgrav 		optind = optreset = 1;
1796d4af9e69SDag-Erling Smørgrav 		__progname = argv[0];
1797b15c8340SDag-Erling Smørgrav #ifdef WITH_SELINUX
1798b15c8340SDag-Erling Smørgrav 		ssh_selinux_change_context("sftpd_t");
1799b15c8340SDag-Erling Smørgrav #endif
1800d4af9e69SDag-Erling Smørgrav 		exit(sftp_server_main(i, argv, s->pw));
1801d4af9e69SDag-Erling Smørgrav 	}
1802d4af9e69SDag-Erling Smørgrav 
1803b15c8340SDag-Erling Smørgrav 	fflush(NULL);
1804b15c8340SDag-Erling Smørgrav 
1805af12a3e7SDag-Erling Smørgrav 	if (options.use_login) {
180680628bacSDag-Erling Smørgrav 		launch_login(pw, hostname);
180780628bacSDag-Erling Smørgrav 		/* NEVERREACHED */
1808af12a3e7SDag-Erling Smørgrav 	}
1809af12a3e7SDag-Erling Smørgrav 
1810af12a3e7SDag-Erling Smørgrav 	/* Get the last component of the shell name. */
1811af12a3e7SDag-Erling Smørgrav 	if ((shell0 = strrchr(shell, '/')) != NULL)
1812af12a3e7SDag-Erling Smørgrav 		shell0++;
1813af12a3e7SDag-Erling Smørgrav 	else
1814af12a3e7SDag-Erling Smørgrav 		shell0 = shell;
1815af12a3e7SDag-Erling Smørgrav 
1816a04a10f8SKris Kennaway 	/*
1817a04a10f8SKris Kennaway 	 * If we have no command, execute the shell.  In this case, the shell
1818a04a10f8SKris Kennaway 	 * name to be passed in argv[0] is preceded by '-' to indicate that
1819a04a10f8SKris Kennaway 	 * this is a login shell.
1820a04a10f8SKris Kennaway 	 */
1821a04a10f8SKris Kennaway 	if (!command) {
1822af12a3e7SDag-Erling Smørgrav 		char argv0[256];
1823a04a10f8SKris Kennaway 
1824a04a10f8SKris Kennaway 		/* Start the shell.  Set initial character to '-'. */
1825af12a3e7SDag-Erling Smørgrav 		argv0[0] = '-';
1826af12a3e7SDag-Erling Smørgrav 
1827af12a3e7SDag-Erling Smørgrav 		if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1)
1828af12a3e7SDag-Erling Smørgrav 		    >= sizeof(argv0) - 1) {
1829af12a3e7SDag-Erling Smørgrav 			errno = EINVAL;
1830af12a3e7SDag-Erling Smørgrav 			perror(shell);
1831af12a3e7SDag-Erling Smørgrav 			exit(1);
1832af12a3e7SDag-Erling Smørgrav 		}
1833a04a10f8SKris Kennaway 
1834a04a10f8SKris Kennaway 		/* Execute the shell. */
1835af12a3e7SDag-Erling Smørgrav 		argv[0] = argv0;
1836a04a10f8SKris Kennaway 		argv[1] = NULL;
1837a04a10f8SKris Kennaway 		execve(shell, argv, env);
1838a04a10f8SKris Kennaway 
1839a04a10f8SKris Kennaway 		/* Executing the shell failed. */
1840a04a10f8SKris Kennaway 		perror(shell);
1841a04a10f8SKris Kennaway 		exit(1);
1842a04a10f8SKris Kennaway 	}
1843a04a10f8SKris Kennaway 	/*
1844a04a10f8SKris Kennaway 	 * Execute the command using the user's shell.  This uses the -c
1845a04a10f8SKris Kennaway 	 * option to execute the command.
1846a04a10f8SKris Kennaway 	 */
1847af12a3e7SDag-Erling Smørgrav 	argv[0] = (char *) shell0;
1848a04a10f8SKris Kennaway 	argv[1] = "-c";
1849a04a10f8SKris Kennaway 	argv[2] = (char *) command;
1850a04a10f8SKris Kennaway 	argv[3] = NULL;
1851a04a10f8SKris Kennaway 	execve(shell, argv, env);
1852a04a10f8SKris Kennaway 	perror(shell);
1853a04a10f8SKris Kennaway 	exit(1);
1854a04a10f8SKris Kennaway }
1855a04a10f8SKris Kennaway 
1856d4af9e69SDag-Erling Smørgrav void
1857d4af9e69SDag-Erling Smørgrav session_unused(int id)
1858d4af9e69SDag-Erling Smørgrav {
1859d4af9e69SDag-Erling Smørgrav 	debug3("%s: session id %d unused", __func__, id);
1860d4af9e69SDag-Erling Smørgrav 	if (id >= options.max_sessions ||
1861d4af9e69SDag-Erling Smørgrav 	    id >= sessions_nalloc) {
1862d4af9e69SDag-Erling Smørgrav 		fatal("%s: insane session id %d (max %d nalloc %d)",
1863d4af9e69SDag-Erling Smørgrav 		    __func__, id, options.max_sessions, sessions_nalloc);
1864d4af9e69SDag-Erling Smørgrav 	}
1865d4af9e69SDag-Erling Smørgrav 	bzero(&sessions[id], sizeof(*sessions));
1866d4af9e69SDag-Erling Smørgrav 	sessions[id].self = id;
1867d4af9e69SDag-Erling Smørgrav 	sessions[id].used = 0;
1868d4af9e69SDag-Erling Smørgrav 	sessions[id].chanid = -1;
1869d4af9e69SDag-Erling Smørgrav 	sessions[id].ptyfd = -1;
1870d4af9e69SDag-Erling Smørgrav 	sessions[id].ttyfd = -1;
1871d4af9e69SDag-Erling Smørgrav 	sessions[id].ptymaster = -1;
1872d4af9e69SDag-Erling Smørgrav 	sessions[id].x11_chanids = NULL;
1873d4af9e69SDag-Erling Smørgrav 	sessions[id].next_unused = sessions_first_unused;
1874d4af9e69SDag-Erling Smørgrav 	sessions_first_unused = id;
1875d4af9e69SDag-Erling Smørgrav }
1876d4af9e69SDag-Erling Smørgrav 
1877a04a10f8SKris Kennaway Session *
1878a04a10f8SKris Kennaway session_new(void)
1879a04a10f8SKris Kennaway {
1880d4af9e69SDag-Erling Smørgrav 	Session *s, *tmp;
1881d4af9e69SDag-Erling Smørgrav 
1882d4af9e69SDag-Erling Smørgrav 	if (sessions_first_unused == -1) {
1883d4af9e69SDag-Erling Smørgrav 		if (sessions_nalloc >= options.max_sessions)
1884a04a10f8SKris Kennaway 			return NULL;
1885d4af9e69SDag-Erling Smørgrav 		debug2("%s: allocate (allocated %d max %d)",
1886d4af9e69SDag-Erling Smørgrav 		    __func__, sessions_nalloc, options.max_sessions);
1887d4af9e69SDag-Erling Smørgrav 		tmp = xrealloc(sessions, sessions_nalloc + 1,
1888d4af9e69SDag-Erling Smørgrav 		    sizeof(*sessions));
1889d4af9e69SDag-Erling Smørgrav 		if (tmp == NULL) {
1890d4af9e69SDag-Erling Smørgrav 			error("%s: cannot allocate %d sessions",
1891d4af9e69SDag-Erling Smørgrav 			    __func__, sessions_nalloc + 1);
1892d4af9e69SDag-Erling Smørgrav 			return NULL;
1893d4af9e69SDag-Erling Smørgrav 		}
1894d4af9e69SDag-Erling Smørgrav 		sessions = tmp;
1895d4af9e69SDag-Erling Smørgrav 		session_unused(sessions_nalloc++);
1896d4af9e69SDag-Erling Smørgrav 	}
1897d4af9e69SDag-Erling Smørgrav 
1898d4af9e69SDag-Erling Smørgrav 	if (sessions_first_unused >= sessions_nalloc ||
1899d4af9e69SDag-Erling Smørgrav 	    sessions_first_unused < 0) {
1900d4af9e69SDag-Erling Smørgrav 		fatal("%s: insane first_unused %d max %d nalloc %d",
1901d4af9e69SDag-Erling Smørgrav 		    __func__, sessions_first_unused, options.max_sessions,
1902d4af9e69SDag-Erling Smørgrav 		    sessions_nalloc);
1903d4af9e69SDag-Erling Smørgrav 	}
1904d4af9e69SDag-Erling Smørgrav 
1905d4af9e69SDag-Erling Smørgrav 	s = &sessions[sessions_first_unused];
1906d4af9e69SDag-Erling Smørgrav 	if (s->used) {
1907d4af9e69SDag-Erling Smørgrav 		fatal("%s: session %d already used",
1908d4af9e69SDag-Erling Smørgrav 		    __func__, sessions_first_unused);
1909d4af9e69SDag-Erling Smørgrav 	}
1910d4af9e69SDag-Erling Smørgrav 	sessions_first_unused = s->next_unused;
1911d4af9e69SDag-Erling Smørgrav 	s->used = 1;
1912d4af9e69SDag-Erling Smørgrav 	s->next_unused = -1;
1913d4af9e69SDag-Erling Smørgrav 	debug("session_new: session %d", s->self);
1914d4af9e69SDag-Erling Smørgrav 
1915d4af9e69SDag-Erling Smørgrav 	return s;
1916a04a10f8SKris Kennaway }
1917a04a10f8SKris Kennaway 
1918af12a3e7SDag-Erling Smørgrav static void
1919a04a10f8SKris Kennaway session_dump(void)
1920a04a10f8SKris Kennaway {
1921a04a10f8SKris Kennaway 	int i;
1922d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
1923a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1924d4af9e69SDag-Erling Smørgrav 
1925d4af9e69SDag-Erling Smørgrav 		debug("dump: used %d next_unused %d session %d %p "
1926d4af9e69SDag-Erling Smørgrav 		    "channel %d pid %ld",
1927a04a10f8SKris Kennaway 		    s->used,
1928d4af9e69SDag-Erling Smørgrav 		    s->next_unused,
1929a04a10f8SKris Kennaway 		    s->self,
1930a04a10f8SKris Kennaway 		    s,
1931a04a10f8SKris Kennaway 		    s->chanid,
193280628bacSDag-Erling Smørgrav 		    (long)s->pid);
1933a04a10f8SKris Kennaway 	}
1934a04a10f8SKris Kennaway }
1935a04a10f8SKris Kennaway 
1936a04a10f8SKris Kennaway int
1937af12a3e7SDag-Erling Smørgrav session_open(Authctxt *authctxt, int chanid)
1938a04a10f8SKris Kennaway {
1939a04a10f8SKris Kennaway 	Session *s = session_new();
1940a04a10f8SKris Kennaway 	debug("session_open: channel %d", chanid);
1941a04a10f8SKris Kennaway 	if (s == NULL) {
1942a04a10f8SKris Kennaway 		error("no more sessions");
1943a04a10f8SKris Kennaway 		return 0;
1944a04a10f8SKris Kennaway 	}
1945af12a3e7SDag-Erling Smørgrav 	s->authctxt = authctxt;
1946af12a3e7SDag-Erling Smørgrav 	s->pw = authctxt->pw;
19471ec0d754SDag-Erling Smørgrav 	if (s->pw == NULL || !authctxt->valid)
1948ca3176e7SBrian Feldman 		fatal("no user for session %d", s->self);
1949a04a10f8SKris Kennaway 	debug("session_open: session %d: link with channel %d", s->self, chanid);
1950a04a10f8SKris Kennaway 	s->chanid = chanid;
1951a04a10f8SKris Kennaway 	return 1;
1952a04a10f8SKris Kennaway }
1953a04a10f8SKris Kennaway 
195480628bacSDag-Erling Smørgrav Session *
195580628bacSDag-Erling Smørgrav session_by_tty(char *tty)
195680628bacSDag-Erling Smørgrav {
195780628bacSDag-Erling Smørgrav 	int i;
1958d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
195980628bacSDag-Erling Smørgrav 		Session *s = &sessions[i];
196080628bacSDag-Erling Smørgrav 		if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) {
196180628bacSDag-Erling Smørgrav 			debug("session_by_tty: session %d tty %s", i, tty);
196280628bacSDag-Erling Smørgrav 			return s;
196380628bacSDag-Erling Smørgrav 		}
196480628bacSDag-Erling Smørgrav 	}
196580628bacSDag-Erling Smørgrav 	debug("session_by_tty: unknown tty %.100s", tty);
196680628bacSDag-Erling Smørgrav 	session_dump();
196780628bacSDag-Erling Smørgrav 	return NULL;
196880628bacSDag-Erling Smørgrav }
196980628bacSDag-Erling Smørgrav 
1970af12a3e7SDag-Erling Smørgrav static Session *
1971a04a10f8SKris Kennaway session_by_channel(int id)
1972a04a10f8SKris Kennaway {
1973a04a10f8SKris Kennaway 	int i;
1974d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
1975a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1976a04a10f8SKris Kennaway 		if (s->used && s->chanid == id) {
1977d4af9e69SDag-Erling Smørgrav 			debug("session_by_channel: session %d channel %d",
1978d4af9e69SDag-Erling Smørgrav 			    i, id);
1979a04a10f8SKris Kennaway 			return s;
1980a04a10f8SKris Kennaway 		}
1981a04a10f8SKris Kennaway 	}
1982a04a10f8SKris Kennaway 	debug("session_by_channel: unknown channel %d", id);
1983a04a10f8SKris Kennaway 	session_dump();
1984a04a10f8SKris Kennaway 	return NULL;
1985a04a10f8SKris Kennaway }
1986a04a10f8SKris Kennaway 
1987af12a3e7SDag-Erling Smørgrav static Session *
1988d4ecd108SDag-Erling Smørgrav session_by_x11_channel(int id)
1989d4ecd108SDag-Erling Smørgrav {
1990d4ecd108SDag-Erling Smørgrav 	int i, j;
1991d4ecd108SDag-Erling Smørgrav 
1992d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
1993d4ecd108SDag-Erling Smørgrav 		Session *s = &sessions[i];
1994d4ecd108SDag-Erling Smørgrav 
1995d4ecd108SDag-Erling Smørgrav 		if (s->x11_chanids == NULL || !s->used)
1996d4ecd108SDag-Erling Smørgrav 			continue;
1997d4ecd108SDag-Erling Smørgrav 		for (j = 0; s->x11_chanids[j] != -1; j++) {
1998d4ecd108SDag-Erling Smørgrav 			if (s->x11_chanids[j] == id) {
1999d4ecd108SDag-Erling Smørgrav 				debug("session_by_x11_channel: session %d "
2000d4ecd108SDag-Erling Smørgrav 				    "channel %d", s->self, id);
2001d4ecd108SDag-Erling Smørgrav 				return s;
2002d4ecd108SDag-Erling Smørgrav 			}
2003d4ecd108SDag-Erling Smørgrav 		}
2004d4ecd108SDag-Erling Smørgrav 	}
2005d4ecd108SDag-Erling Smørgrav 	debug("session_by_x11_channel: unknown channel %d", id);
2006d4ecd108SDag-Erling Smørgrav 	session_dump();
2007d4ecd108SDag-Erling Smørgrav 	return NULL;
2008d4ecd108SDag-Erling Smørgrav }
2009d4ecd108SDag-Erling Smørgrav 
2010d4ecd108SDag-Erling Smørgrav static Session *
2011a04a10f8SKris Kennaway session_by_pid(pid_t pid)
2012a04a10f8SKris Kennaway {
2013a04a10f8SKris Kennaway 	int i;
201480628bacSDag-Erling Smørgrav 	debug("session_by_pid: pid %ld", (long)pid);
2015d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
2016a04a10f8SKris Kennaway 		Session *s = &sessions[i];
2017a04a10f8SKris Kennaway 		if (s->used && s->pid == pid)
2018a04a10f8SKris Kennaway 			return s;
2019a04a10f8SKris Kennaway 	}
202080628bacSDag-Erling Smørgrav 	error("session_by_pid: unknown pid %ld", (long)pid);
2021a04a10f8SKris Kennaway 	session_dump();
2022a04a10f8SKris Kennaway 	return NULL;
2023a04a10f8SKris Kennaway }
2024a04a10f8SKris Kennaway 
2025af12a3e7SDag-Erling Smørgrav static int
2026a04a10f8SKris Kennaway session_window_change_req(Session *s)
2027a04a10f8SKris Kennaway {
2028a04a10f8SKris Kennaway 	s->col = packet_get_int();
2029a04a10f8SKris Kennaway 	s->row = packet_get_int();
2030a04a10f8SKris Kennaway 	s->xpixel = packet_get_int();
2031a04a10f8SKris Kennaway 	s->ypixel = packet_get_int();
2032af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2033a04a10f8SKris Kennaway 	pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
2034a04a10f8SKris Kennaway 	return 1;
2035a04a10f8SKris Kennaway }
2036a04a10f8SKris Kennaway 
2037af12a3e7SDag-Erling Smørgrav static int
2038a04a10f8SKris Kennaway session_pty_req(Session *s)
2039a04a10f8SKris Kennaway {
2040ca3176e7SBrian Feldman 	u_int len;
2041ca3176e7SBrian Feldman 	int n_bytes;
2042a04a10f8SKris Kennaway 
2043af12a3e7SDag-Erling Smørgrav 	if (no_pty_flag) {
2044af12a3e7SDag-Erling Smørgrav 		debug("Allocating a pty not permitted for this authentication.");
2045c2d3a559SKris Kennaway 		return 0;
2046af12a3e7SDag-Erling Smørgrav 	}
2047af12a3e7SDag-Erling Smørgrav 	if (s->ttyfd != -1) {
2048af12a3e7SDag-Erling Smørgrav 		packet_disconnect("Protocol error: you already have a pty.");
2049a04a10f8SKris Kennaway 		return 0;
2050af12a3e7SDag-Erling Smørgrav 	}
2051af12a3e7SDag-Erling Smørgrav 
2052a04a10f8SKris Kennaway 	s->term = packet_get_string(&len);
2053af12a3e7SDag-Erling Smørgrav 
2054af12a3e7SDag-Erling Smørgrav 	if (compat20) {
2055a04a10f8SKris Kennaway 		s->col = packet_get_int();
2056a04a10f8SKris Kennaway 		s->row = packet_get_int();
2057af12a3e7SDag-Erling Smørgrav 	} else {
2058af12a3e7SDag-Erling Smørgrav 		s->row = packet_get_int();
2059af12a3e7SDag-Erling Smørgrav 		s->col = packet_get_int();
2060af12a3e7SDag-Erling Smørgrav 	}
2061a04a10f8SKris Kennaway 	s->xpixel = packet_get_int();
2062a04a10f8SKris Kennaway 	s->ypixel = packet_get_int();
2063a04a10f8SKris Kennaway 
2064a04a10f8SKris Kennaway 	if (strcmp(s->term, "") == 0) {
2065a04a10f8SKris Kennaway 		xfree(s->term);
2066a04a10f8SKris Kennaway 		s->term = NULL;
2067a04a10f8SKris Kennaway 	}
2068af12a3e7SDag-Erling Smørgrav 
2069a04a10f8SKris Kennaway 	/* Allocate a pty and open it. */
2070af12a3e7SDag-Erling Smørgrav 	debug("Allocating pty.");
2071d4af9e69SDag-Erling Smørgrav 	if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
2072d4af9e69SDag-Erling Smørgrav 	    sizeof(s->tty)))) {
2073af12a3e7SDag-Erling Smørgrav 		if (s->term)
2074a04a10f8SKris Kennaway 			xfree(s->term);
2075a04a10f8SKris Kennaway 		s->term = NULL;
2076a04a10f8SKris Kennaway 		s->ptyfd = -1;
2077a04a10f8SKris Kennaway 		s->ttyfd = -1;
2078a04a10f8SKris Kennaway 		error("session_pty_req: session %d alloc failed", s->self);
2079a04a10f8SKris Kennaway 		return 0;
2080a04a10f8SKris Kennaway 	}
2081a04a10f8SKris Kennaway 	debug("session_pty_req: session %d alloc %s", s->self, s->tty);
2082af12a3e7SDag-Erling Smørgrav 
2083af12a3e7SDag-Erling Smørgrav 	/* for SSH1 the tty modes length is not given */
2084af12a3e7SDag-Erling Smørgrav 	if (!compat20)
2085af12a3e7SDag-Erling Smørgrav 		n_bytes = packet_remaining();
2086af12a3e7SDag-Erling Smørgrav 	tty_parse_modes(s->ttyfd, &n_bytes);
2087af12a3e7SDag-Erling Smørgrav 
208880628bacSDag-Erling Smørgrav 	if (!use_privsep)
2089a04a10f8SKris Kennaway 		pty_setowner(s->pw, s->tty);
2090af12a3e7SDag-Erling Smørgrav 
2091af12a3e7SDag-Erling Smørgrav 	/* Set window size from the packet. */
2092a04a10f8SKris Kennaway 	pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
2093a04a10f8SKris Kennaway 
2094af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2095a04a10f8SKris Kennaway 	session_proctitle(s);
2096a04a10f8SKris Kennaway 	return 1;
2097a04a10f8SKris Kennaway }
2098a04a10f8SKris Kennaway 
2099af12a3e7SDag-Erling Smørgrav static int
2100a04a10f8SKris Kennaway session_subsystem_req(Session *s)
2101a04a10f8SKris Kennaway {
2102af12a3e7SDag-Erling Smørgrav 	struct stat st;
2103ca3176e7SBrian Feldman 	u_int len;
2104a04a10f8SKris Kennaway 	int success = 0;
2105333ee039SDag-Erling Smørgrav 	char *prog, *cmd, *subsys = packet_get_string(&len);
2106d4ecd108SDag-Erling Smørgrav 	u_int i;
2107a04a10f8SKris Kennaway 
2108af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2109e2f6069cSDag-Erling Smørgrav 	logit("subsystem request for %.100s by user %s", subsys,
2110e2f6069cSDag-Erling Smørgrav 	    s->pw->pw_name);
2111a04a10f8SKris Kennaway 
2112c2d3a559SKris Kennaway 	for (i = 0; i < options.num_subsystems; i++) {
2113c2d3a559SKris Kennaway 		if (strcmp(subsys, options.subsystem_name[i]) == 0) {
2114333ee039SDag-Erling Smørgrav 			prog = options.subsystem_command[i];
2115333ee039SDag-Erling Smørgrav 			cmd = options.subsystem_args[i];
2116b15c8340SDag-Erling Smørgrav 			if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) {
2117d4af9e69SDag-Erling Smørgrav 				s->is_subsystem = SUBSYSTEM_INT_SFTP;
2118b15c8340SDag-Erling Smørgrav 				debug("subsystem: %s", prog);
2119d4af9e69SDag-Erling Smørgrav 			} else {
2120b15c8340SDag-Erling Smørgrav 				if (stat(prog, &st) < 0)
2121b15c8340SDag-Erling Smørgrav 					debug("subsystem: cannot stat %s: %s",
2122b15c8340SDag-Erling Smørgrav 					    prog, strerror(errno));
2123d4af9e69SDag-Erling Smørgrav 				s->is_subsystem = SUBSYSTEM_EXT;
2124af12a3e7SDag-Erling Smørgrav 				debug("subsystem: exec() %s", cmd);
2125b15c8340SDag-Erling Smørgrav 			}
2126d4af9e69SDag-Erling Smørgrav 			success = do_exec(s, cmd) == 0;
2127af12a3e7SDag-Erling Smørgrav 			break;
2128c2d3a559SKris Kennaway 		}
2129c2d3a559SKris Kennaway 	}
2130c2d3a559SKris Kennaway 
2131c2d3a559SKris Kennaway 	if (!success)
2132cf2b5f3bSDag-Erling Smørgrav 		logit("subsystem request for %.100s failed, subsystem not found",
2133af12a3e7SDag-Erling Smørgrav 		    subsys);
2134c2d3a559SKris Kennaway 
2135a04a10f8SKris Kennaway 	xfree(subsys);
2136a04a10f8SKris Kennaway 	return success;
2137a04a10f8SKris Kennaway }
2138a04a10f8SKris Kennaway 
2139af12a3e7SDag-Erling Smørgrav static int
2140a04a10f8SKris Kennaway session_x11_req(Session *s)
2141a04a10f8SKris Kennaway {
2142af12a3e7SDag-Erling Smørgrav 	int success;
2143a04a10f8SKris Kennaway 
2144d4ecd108SDag-Erling Smørgrav 	if (s->auth_proto != NULL || s->auth_data != NULL) {
2145d4ecd108SDag-Erling Smørgrav 		error("session_x11_req: session %d: "
2146b74df5b2SDag-Erling Smørgrav 		    "x11 forwarding already active", s->self);
2147d4ecd108SDag-Erling Smørgrav 		return 0;
2148d4ecd108SDag-Erling Smørgrav 	}
2149a04a10f8SKris Kennaway 	s->single_connection = packet_get_char();
2150a04a10f8SKris Kennaway 	s->auth_proto = packet_get_string(NULL);
2151a04a10f8SKris Kennaway 	s->auth_data = packet_get_string(NULL);
2152a04a10f8SKris Kennaway 	s->screen = packet_get_int();
2153af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2154a04a10f8SKris Kennaway 
2155af12a3e7SDag-Erling Smørgrav 	success = session_setup_x11fwd(s);
2156af12a3e7SDag-Erling Smørgrav 	if (!success) {
2157a04a10f8SKris Kennaway 		xfree(s->auth_proto);
2158a04a10f8SKris Kennaway 		xfree(s->auth_data);
2159af12a3e7SDag-Erling Smørgrav 		s->auth_proto = NULL;
2160af12a3e7SDag-Erling Smørgrav 		s->auth_data = NULL;
2161a04a10f8SKris Kennaway 	}
2162af12a3e7SDag-Erling Smørgrav 	return success;
2163a04a10f8SKris Kennaway }
2164a04a10f8SKris Kennaway 
2165af12a3e7SDag-Erling Smørgrav static int
2166c2d3a559SKris Kennaway session_shell_req(Session *s)
2167c2d3a559SKris Kennaway {
2168af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2169d4af9e69SDag-Erling Smørgrav 	return do_exec(s, NULL) == 0;
2170c2d3a559SKris Kennaway }
2171c2d3a559SKris Kennaway 
2172af12a3e7SDag-Erling Smørgrav static int
2173c2d3a559SKris Kennaway session_exec_req(Session *s)
2174c2d3a559SKris Kennaway {
2175d4af9e69SDag-Erling Smørgrav 	u_int len, success;
2176d4af9e69SDag-Erling Smørgrav 
2177c2d3a559SKris Kennaway 	char *command = packet_get_string(&len);
2178af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2179d4af9e69SDag-Erling Smørgrav 	success = do_exec(s, command) == 0;
2180c2d3a559SKris Kennaway 	xfree(command);
2181d4af9e69SDag-Erling Smørgrav 	return success;
2182c2d3a559SKris Kennaway }
2183c2d3a559SKris Kennaway 
2184af12a3e7SDag-Erling Smørgrav static int
2185cf2b5f3bSDag-Erling Smørgrav session_break_req(Session *s)
2186cf2b5f3bSDag-Erling Smørgrav {
2187cf2b5f3bSDag-Erling Smørgrav 
218821e764dfSDag-Erling Smørgrav 	packet_get_int();	/* ignored */
2189cf2b5f3bSDag-Erling Smørgrav 	packet_check_eom();
2190cf2b5f3bSDag-Erling Smørgrav 
2191462c32cbSDag-Erling Smørgrav 	if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) < 0)
2192cf2b5f3bSDag-Erling Smørgrav 		return 0;
2193cf2b5f3bSDag-Erling Smørgrav 	return 1;
2194cf2b5f3bSDag-Erling Smørgrav }
2195cf2b5f3bSDag-Erling Smørgrav 
2196cf2b5f3bSDag-Erling Smørgrav static int
219721e764dfSDag-Erling Smørgrav session_env_req(Session *s)
219821e764dfSDag-Erling Smørgrav {
219921e764dfSDag-Erling Smørgrav 	char *name, *val;
220021e764dfSDag-Erling Smørgrav 	u_int name_len, val_len, i;
220121e764dfSDag-Erling Smørgrav 
220221e764dfSDag-Erling Smørgrav 	name = packet_get_string(&name_len);
220321e764dfSDag-Erling Smørgrav 	val = packet_get_string(&val_len);
220421e764dfSDag-Erling Smørgrav 	packet_check_eom();
220521e764dfSDag-Erling Smørgrav 
220621e764dfSDag-Erling Smørgrav 	/* Don't set too many environment variables */
220721e764dfSDag-Erling Smørgrav 	if (s->num_env > 128) {
220821e764dfSDag-Erling Smørgrav 		debug2("Ignoring env request %s: too many env vars", name);
220921e764dfSDag-Erling Smørgrav 		goto fail;
221021e764dfSDag-Erling Smørgrav 	}
221121e764dfSDag-Erling Smørgrav 
221221e764dfSDag-Erling Smørgrav 	for (i = 0; i < options.num_accept_env; i++) {
221321e764dfSDag-Erling Smørgrav 		if (match_pattern(name, options.accept_env[i])) {
221421e764dfSDag-Erling Smørgrav 			debug2("Setting env %d: %s=%s", s->num_env, name, val);
2215333ee039SDag-Erling Smørgrav 			s->env = xrealloc(s->env, s->num_env + 1,
2216333ee039SDag-Erling Smørgrav 			    sizeof(*s->env));
221721e764dfSDag-Erling Smørgrav 			s->env[s->num_env].name = name;
221821e764dfSDag-Erling Smørgrav 			s->env[s->num_env].val = val;
221921e764dfSDag-Erling Smørgrav 			s->num_env++;
222021e764dfSDag-Erling Smørgrav 			return (1);
222121e764dfSDag-Erling Smørgrav 		}
222221e764dfSDag-Erling Smørgrav 	}
222321e764dfSDag-Erling Smørgrav 	debug2("Ignoring env request %s: disallowed name", name);
222421e764dfSDag-Erling Smørgrav 
222521e764dfSDag-Erling Smørgrav  fail:
222621e764dfSDag-Erling Smørgrav 	xfree(name);
222721e764dfSDag-Erling Smørgrav 	xfree(val);
222821e764dfSDag-Erling Smørgrav 	return (0);
222921e764dfSDag-Erling Smørgrav }
223021e764dfSDag-Erling Smørgrav 
223121e764dfSDag-Erling Smørgrav static int
2232ca3176e7SBrian Feldman session_auth_agent_req(Session *s)
2233ca3176e7SBrian Feldman {
2234ca3176e7SBrian Feldman 	static int called = 0;
2235af12a3e7SDag-Erling Smørgrav 	packet_check_eom();
2236d4af9e69SDag-Erling Smørgrav 	if (no_agent_forwarding_flag || !options.allow_agent_forwarding) {
2237ca3176e7SBrian Feldman 		debug("session_auth_agent_req: no_agent_forwarding_flag");
2238ca3176e7SBrian Feldman 		return 0;
2239ca3176e7SBrian Feldman 	}
2240ca3176e7SBrian Feldman 	if (called) {
2241ca3176e7SBrian Feldman 		return 0;
2242ca3176e7SBrian Feldman 	} else {
2243ca3176e7SBrian Feldman 		called = 1;
2244ca3176e7SBrian Feldman 		return auth_input_request_forwarding(s->pw);
2245ca3176e7SBrian Feldman 	}
2246ca3176e7SBrian Feldman }
2247ca3176e7SBrian Feldman 
2248af12a3e7SDag-Erling Smørgrav int
2249af12a3e7SDag-Erling Smørgrav session_input_channel_req(Channel *c, const char *rtype)
2250a04a10f8SKris Kennaway {
2251a04a10f8SKris Kennaway 	int success = 0;
2252a04a10f8SKris Kennaway 	Session *s;
2253a04a10f8SKris Kennaway 
2254af12a3e7SDag-Erling Smørgrav 	if ((s = session_by_channel(c->self)) == NULL) {
2255cf2b5f3bSDag-Erling Smørgrav 		logit("session_input_channel_req: no session %d req %.100s",
2256af12a3e7SDag-Erling Smørgrav 		    c->self, rtype);
2257af12a3e7SDag-Erling Smørgrav 		return 0;
2258af12a3e7SDag-Erling Smørgrav 	}
2259af12a3e7SDag-Erling Smørgrav 	debug("session_input_channel_req: session %d req %s", s->self, rtype);
2260a04a10f8SKris Kennaway 
2261a04a10f8SKris Kennaway 	/*
2262ca3176e7SBrian Feldman 	 * a session is in LARVAL state until a shell, a command
2263ca3176e7SBrian Feldman 	 * or a subsystem is executed
2264a04a10f8SKris Kennaway 	 */
2265a04a10f8SKris Kennaway 	if (c->type == SSH_CHANNEL_LARVAL) {
2266a04a10f8SKris Kennaway 		if (strcmp(rtype, "shell") == 0) {
2267c2d3a559SKris Kennaway 			success = session_shell_req(s);
2268a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "exec") == 0) {
2269c2d3a559SKris Kennaway 			success = session_exec_req(s);
2270a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "pty-req") == 0) {
2271a04a10f8SKris Kennaway 			success = session_pty_req(s);
2272a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "x11-req") == 0) {
2273a04a10f8SKris Kennaway 			success = session_x11_req(s);
2274ca3176e7SBrian Feldman 		} else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
2275ca3176e7SBrian Feldman 			success = session_auth_agent_req(s);
2276a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "subsystem") == 0) {
2277a04a10f8SKris Kennaway 			success = session_subsystem_req(s);
227821e764dfSDag-Erling Smørgrav 		} else if (strcmp(rtype, "env") == 0) {
227921e764dfSDag-Erling Smørgrav 			success = session_env_req(s);
2280a04a10f8SKris Kennaway 		}
2281a04a10f8SKris Kennaway 	}
2282a04a10f8SKris Kennaway 	if (strcmp(rtype, "window-change") == 0) {
2283a04a10f8SKris Kennaway 		success = session_window_change_req(s);
228421e764dfSDag-Erling Smørgrav 	} else if (strcmp(rtype, "break") == 0) {
228521e764dfSDag-Erling Smørgrav 		success = session_break_req(s);
2286a04a10f8SKris Kennaway 	}
228721e764dfSDag-Erling Smørgrav 
2288af12a3e7SDag-Erling Smørgrav 	return success;
2289a04a10f8SKris Kennaway }
2290a04a10f8SKris Kennaway 
2291a04a10f8SKris Kennaway void
2292e2f6069cSDag-Erling Smørgrav session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr,
2293e2f6069cSDag-Erling Smørgrav     int is_tty)
2294a04a10f8SKris Kennaway {
2295a04a10f8SKris Kennaway 	if (!compat20)
2296a04a10f8SKris Kennaway 		fatal("session_set_fds: called for proto != 2.0");
2297a04a10f8SKris Kennaway 	/*
2298a04a10f8SKris Kennaway 	 * now that have a child and a pipe to the child,
2299a04a10f8SKris Kennaway 	 * we can activate our channel and register the fd's
2300a04a10f8SKris Kennaway 	 */
2301a04a10f8SKris Kennaway 	if (s->chanid == -1)
2302a04a10f8SKris Kennaway 		fatal("no channel for session %d", s->self);
230389986192SBrooks Davis 	if (options.hpn_disabled)
230489986192SBrooks Davis 		channel_set_fds(s->chanid, fdout, fdin, fderr,
2305e2f6069cSDag-Erling Smørgrav 		    ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
2306d4af9e69SDag-Erling Smørgrav 		    1, is_tty, CHAN_SES_WINDOW_DEFAULT);
230789986192SBrooks Davis 	else
230889986192SBrooks Davis 		channel_set_fds(s->chanid, fdout, fdin, fderr,
230989986192SBrooks Davis 		    ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
231089986192SBrooks Davis 		    1, is_tty, options.hpn_buffer_size);
2311a04a10f8SKris Kennaway }
2312a04a10f8SKris Kennaway 
2313af12a3e7SDag-Erling Smørgrav /*
2314af12a3e7SDag-Erling Smørgrav  * Function to perform pty cleanup. Also called if we get aborted abnormally
2315af12a3e7SDag-Erling Smørgrav  * (e.g., due to a dropped connection).
2316af12a3e7SDag-Erling Smørgrav  */
231780628bacSDag-Erling Smørgrav void
23181ec0d754SDag-Erling Smørgrav session_pty_cleanup2(Session *s)
2319a04a10f8SKris Kennaway {
2320af12a3e7SDag-Erling Smørgrav 	if (s == NULL) {
2321af12a3e7SDag-Erling Smørgrav 		error("session_pty_cleanup: no session");
2322af12a3e7SDag-Erling Smørgrav 		return;
2323af12a3e7SDag-Erling Smørgrav 	}
2324af12a3e7SDag-Erling Smørgrav 	if (s->ttyfd == -1)
2325a04a10f8SKris Kennaway 		return;
2326a04a10f8SKris Kennaway 
2327ca3176e7SBrian Feldman 	debug("session_pty_cleanup: session %d release %s", s->self, s->tty);
2328a04a10f8SKris Kennaway 
2329a04a10f8SKris Kennaway 	/* Record that the user has logged out. */
2330af12a3e7SDag-Erling Smørgrav 	if (s->pid != 0)
2331989dd127SDag-Erling Smørgrav 		record_logout(s->pid, s->tty, s->pw->pw_name);
2332a04a10f8SKris Kennaway 
2333a04a10f8SKris Kennaway 	/* Release the pseudo-tty. */
233480628bacSDag-Erling Smørgrav 	if (getuid() == 0)
2335a04a10f8SKris Kennaway 		pty_release(s->tty);
2336a04a10f8SKris Kennaway 
2337a04a10f8SKris Kennaway 	/*
2338a04a10f8SKris Kennaway 	 * Close the server side of the socket pairs.  We must do this after
2339a04a10f8SKris Kennaway 	 * the pty cleanup, so that another process doesn't get this pty
2340a04a10f8SKris Kennaway 	 * while we're still cleaning up.
2341a04a10f8SKris Kennaway 	 */
2342d4af9e69SDag-Erling Smørgrav 	if (s->ptymaster != -1 && close(s->ptymaster) < 0)
2343d4af9e69SDag-Erling Smørgrav 		error("close(s->ptymaster/%d): %s",
2344d4af9e69SDag-Erling Smørgrav 		    s->ptymaster, strerror(errno));
2345af12a3e7SDag-Erling Smørgrav 
2346af12a3e7SDag-Erling Smørgrav 	/* unlink pty from session */
2347af12a3e7SDag-Erling Smørgrav 	s->ttyfd = -1;
2348a04a10f8SKris Kennaway }
2349a04a10f8SKris Kennaway 
235080628bacSDag-Erling Smørgrav void
23511ec0d754SDag-Erling Smørgrav session_pty_cleanup(Session *s)
235280628bacSDag-Erling Smørgrav {
23531ec0d754SDag-Erling Smørgrav 	PRIVSEP(session_pty_cleanup2(s));
235480628bacSDag-Erling Smørgrav }
235580628bacSDag-Erling Smørgrav 
2356f388f5efSDag-Erling Smørgrav static char *
2357f388f5efSDag-Erling Smørgrav sig2name(int sig)
2358f388f5efSDag-Erling Smørgrav {
2359f388f5efSDag-Erling Smørgrav #define SSH_SIG(x) if (sig == SIG ## x) return #x
2360f388f5efSDag-Erling Smørgrav 	SSH_SIG(ABRT);
2361f388f5efSDag-Erling Smørgrav 	SSH_SIG(ALRM);
2362f388f5efSDag-Erling Smørgrav 	SSH_SIG(FPE);
2363f388f5efSDag-Erling Smørgrav 	SSH_SIG(HUP);
2364f388f5efSDag-Erling Smørgrav 	SSH_SIG(ILL);
2365f388f5efSDag-Erling Smørgrav 	SSH_SIG(INT);
2366f388f5efSDag-Erling Smørgrav 	SSH_SIG(KILL);
2367f388f5efSDag-Erling Smørgrav 	SSH_SIG(PIPE);
2368f388f5efSDag-Erling Smørgrav 	SSH_SIG(QUIT);
2369f388f5efSDag-Erling Smørgrav 	SSH_SIG(SEGV);
2370f388f5efSDag-Erling Smørgrav 	SSH_SIG(TERM);
2371f388f5efSDag-Erling Smørgrav 	SSH_SIG(USR1);
2372f388f5efSDag-Erling Smørgrav 	SSH_SIG(USR2);
2373f388f5efSDag-Erling Smørgrav #undef	SSH_SIG
2374f388f5efSDag-Erling Smørgrav 	return "SIG@openssh.com";
2375f388f5efSDag-Erling Smørgrav }
2376f388f5efSDag-Erling Smørgrav 
2377af12a3e7SDag-Erling Smørgrav static void
2378d4ecd108SDag-Erling Smørgrav session_close_x11(int id)
2379d4ecd108SDag-Erling Smørgrav {
2380d4ecd108SDag-Erling Smørgrav 	Channel *c;
2381d4ecd108SDag-Erling Smørgrav 
2382b74df5b2SDag-Erling Smørgrav 	if ((c = channel_by_id(id)) == NULL) {
2383d4ecd108SDag-Erling Smørgrav 		debug("session_close_x11: x11 channel %d missing", id);
2384d4ecd108SDag-Erling Smørgrav 	} else {
2385d4ecd108SDag-Erling Smørgrav 		/* Detach X11 listener */
2386d4ecd108SDag-Erling Smørgrav 		debug("session_close_x11: detach x11 channel %d", id);
2387d4ecd108SDag-Erling Smørgrav 		channel_cancel_cleanup(id);
2388d4ecd108SDag-Erling Smørgrav 		if (c->ostate != CHAN_OUTPUT_CLOSED)
2389d4ecd108SDag-Erling Smørgrav 			chan_mark_dead(c);
2390d4ecd108SDag-Erling Smørgrav 	}
2391d4ecd108SDag-Erling Smørgrav }
2392d4ecd108SDag-Erling Smørgrav 
2393d4ecd108SDag-Erling Smørgrav static void
2394d4ecd108SDag-Erling Smørgrav session_close_single_x11(int id, void *arg)
2395d4ecd108SDag-Erling Smørgrav {
2396d4ecd108SDag-Erling Smørgrav 	Session *s;
2397d4ecd108SDag-Erling Smørgrav 	u_int i;
2398d4ecd108SDag-Erling Smørgrav 
2399d4ecd108SDag-Erling Smørgrav 	debug3("session_close_single_x11: channel %d", id);
2400d4ecd108SDag-Erling Smørgrav 	channel_cancel_cleanup(id);
2401d4ecd108SDag-Erling Smørgrav 	if ((s = session_by_x11_channel(id)) == NULL)
2402d4ecd108SDag-Erling Smørgrav 		fatal("session_close_single_x11: no x11 channel %d", id);
2403d4ecd108SDag-Erling Smørgrav 	for (i = 0; s->x11_chanids[i] != -1; i++) {
2404d4ecd108SDag-Erling Smørgrav 		debug("session_close_single_x11: session %d: "
2405d4ecd108SDag-Erling Smørgrav 		    "closing channel %d", s->self, s->x11_chanids[i]);
2406d4ecd108SDag-Erling Smørgrav 		/*
2407d4ecd108SDag-Erling Smørgrav 		 * The channel "id" is already closing, but make sure we
2408d4ecd108SDag-Erling Smørgrav 		 * close all of its siblings.
2409d4ecd108SDag-Erling Smørgrav 		 */
2410d4ecd108SDag-Erling Smørgrav 		if (s->x11_chanids[i] != id)
2411d4ecd108SDag-Erling Smørgrav 			session_close_x11(s->x11_chanids[i]);
2412d4ecd108SDag-Erling Smørgrav 	}
2413d4ecd108SDag-Erling Smørgrav 	xfree(s->x11_chanids);
2414d4ecd108SDag-Erling Smørgrav 	s->x11_chanids = NULL;
2415d4ecd108SDag-Erling Smørgrav 	if (s->display) {
2416d4ecd108SDag-Erling Smørgrav 		xfree(s->display);
2417d4ecd108SDag-Erling Smørgrav 		s->display = NULL;
2418d4ecd108SDag-Erling Smørgrav 	}
2419d4ecd108SDag-Erling Smørgrav 	if (s->auth_proto) {
2420d4ecd108SDag-Erling Smørgrav 		xfree(s->auth_proto);
2421d4ecd108SDag-Erling Smørgrav 		s->auth_proto = NULL;
2422d4ecd108SDag-Erling Smørgrav 	}
2423d4ecd108SDag-Erling Smørgrav 	if (s->auth_data) {
2424d4ecd108SDag-Erling Smørgrav 		xfree(s->auth_data);
2425d4ecd108SDag-Erling Smørgrav 		s->auth_data = NULL;
2426d4ecd108SDag-Erling Smørgrav 	}
2427d4ecd108SDag-Erling Smørgrav 	if (s->auth_display) {
2428d4ecd108SDag-Erling Smørgrav 		xfree(s->auth_display);
2429d4ecd108SDag-Erling Smørgrav 		s->auth_display = NULL;
2430d4ecd108SDag-Erling Smørgrav 	}
2431d4ecd108SDag-Erling Smørgrav }
2432d4ecd108SDag-Erling Smørgrav 
2433d4ecd108SDag-Erling Smørgrav static void
2434a04a10f8SKris Kennaway session_exit_message(Session *s, int status)
2435a04a10f8SKris Kennaway {
2436a04a10f8SKris Kennaway 	Channel *c;
2437af12a3e7SDag-Erling Smørgrav 
2438af12a3e7SDag-Erling Smørgrav 	if ((c = channel_lookup(s->chanid)) == NULL)
2439af12a3e7SDag-Erling Smørgrav 		fatal("session_exit_message: session %d: no channel %d",
2440a04a10f8SKris Kennaway 		    s->self, s->chanid);
244180628bacSDag-Erling Smørgrav 	debug("session_exit_message: session %d channel %d pid %ld",
244280628bacSDag-Erling Smørgrav 	    s->self, s->chanid, (long)s->pid);
2443a04a10f8SKris Kennaway 
2444a04a10f8SKris Kennaway 	if (WIFEXITED(status)) {
2445af12a3e7SDag-Erling Smørgrav 		channel_request_start(s->chanid, "exit-status", 0);
2446a04a10f8SKris Kennaway 		packet_put_int(WEXITSTATUS(status));
2447a04a10f8SKris Kennaway 		packet_send();
2448a04a10f8SKris Kennaway 	} else if (WIFSIGNALED(status)) {
2449af12a3e7SDag-Erling Smørgrav 		channel_request_start(s->chanid, "exit-signal", 0);
2450f388f5efSDag-Erling Smørgrav 		packet_put_cstring(sig2name(WTERMSIG(status)));
2451989dd127SDag-Erling Smørgrav #ifdef WCOREDUMP
2452d4af9e69SDag-Erling Smørgrav 		packet_put_char(WCOREDUMP(status)? 1 : 0);
2453989dd127SDag-Erling Smørgrav #else /* WCOREDUMP */
2454989dd127SDag-Erling Smørgrav 		packet_put_char(0);
2455989dd127SDag-Erling Smørgrav #endif /* WCOREDUMP */
2456a04a10f8SKris Kennaway 		packet_put_cstring("");
2457a04a10f8SKris Kennaway 		packet_put_cstring("");
2458a04a10f8SKris Kennaway 		packet_send();
2459a04a10f8SKris Kennaway 	} else {
2460a04a10f8SKris Kennaway 		/* Some weird exit cause.  Just exit. */
2461a04a10f8SKris Kennaway 		packet_disconnect("wait returned status %04x.", status);
2462a04a10f8SKris Kennaway 	}
2463a04a10f8SKris Kennaway 
2464a04a10f8SKris Kennaway 	/* disconnect channel */
2465a04a10f8SKris Kennaway 	debug("session_exit_message: release channel %d", s->chanid);
2466b74df5b2SDag-Erling Smørgrav 
2467b74df5b2SDag-Erling Smørgrav 	/*
2468b74df5b2SDag-Erling Smørgrav 	 * Adjust cleanup callback attachment to send close messages when
2469b74df5b2SDag-Erling Smørgrav 	 * the channel gets EOF. The session will be then be closed
2470b74df5b2SDag-Erling Smørgrav 	 * by session_close_by_channel when the childs close their fds.
2471b74df5b2SDag-Erling Smørgrav 	 */
2472b74df5b2SDag-Erling Smørgrav 	channel_register_cleanup(c->self, session_close_by_channel, 1);
2473b74df5b2SDag-Erling Smørgrav 
2474a04a10f8SKris Kennaway 	/*
2475a04a10f8SKris Kennaway 	 * emulate a write failure with 'chan_write_failed', nobody will be
2476a04a10f8SKris Kennaway 	 * interested in data we write.
2477a04a10f8SKris Kennaway 	 * Note that we must not call 'chan_read_failed', since there could
2478a04a10f8SKris Kennaway 	 * be some more data waiting in the pipe.
2479a04a10f8SKris Kennaway 	 */
2480a04a10f8SKris Kennaway 	if (c->ostate != CHAN_OUTPUT_CLOSED)
2481a04a10f8SKris Kennaway 		chan_write_failed(c);
2482a04a10f8SKris Kennaway }
2483a04a10f8SKris Kennaway 
248480628bacSDag-Erling Smørgrav void
2485af12a3e7SDag-Erling Smørgrav session_close(Session *s)
2486a04a10f8SKris Kennaway {
2487d4ecd108SDag-Erling Smørgrav 	u_int i;
248821e764dfSDag-Erling Smørgrav 
248980628bacSDag-Erling Smørgrav 	debug("session_close: session %d pid %ld", s->self, (long)s->pid);
24901ec0d754SDag-Erling Smørgrav 	if (s->ttyfd != -1)
2491af12a3e7SDag-Erling Smørgrav 		session_pty_cleanup(s);
2492a04a10f8SKris Kennaway 	if (s->term)
2493a04a10f8SKris Kennaway 		xfree(s->term);
2494a04a10f8SKris Kennaway 	if (s->display)
2495a04a10f8SKris Kennaway 		xfree(s->display);
2496d4ecd108SDag-Erling Smørgrav 	if (s->x11_chanids)
2497d4ecd108SDag-Erling Smørgrav 		xfree(s->x11_chanids);
2498af12a3e7SDag-Erling Smørgrav 	if (s->auth_display)
2499af12a3e7SDag-Erling Smørgrav 		xfree(s->auth_display);
2500a04a10f8SKris Kennaway 	if (s->auth_data)
2501a04a10f8SKris Kennaway 		xfree(s->auth_data);
2502a04a10f8SKris Kennaway 	if (s->auth_proto)
2503a04a10f8SKris Kennaway 		xfree(s->auth_proto);
2504333ee039SDag-Erling Smørgrav 	if (s->env != NULL) {
250521e764dfSDag-Erling Smørgrav 		for (i = 0; i < s->num_env; i++) {
250621e764dfSDag-Erling Smørgrav 			xfree(s->env[i].name);
250721e764dfSDag-Erling Smørgrav 			xfree(s->env[i].val);
250821e764dfSDag-Erling Smørgrav 		}
250921e764dfSDag-Erling Smørgrav 		xfree(s->env);
2510333ee039SDag-Erling Smørgrav 	}
2511a04a10f8SKris Kennaway 	session_proctitle(s);
2512d4af9e69SDag-Erling Smørgrav 	session_unused(s->self);
2513a04a10f8SKris Kennaway }
2514a04a10f8SKris Kennaway 
2515a04a10f8SKris Kennaway void
2516a04a10f8SKris Kennaway session_close_by_pid(pid_t pid, int status)
2517a04a10f8SKris Kennaway {
2518a04a10f8SKris Kennaway 	Session *s = session_by_pid(pid);
2519a04a10f8SKris Kennaway 	if (s == NULL) {
252080628bacSDag-Erling Smørgrav 		debug("session_close_by_pid: no session for pid %ld",
252180628bacSDag-Erling Smørgrav 		    (long)pid);
2522a04a10f8SKris Kennaway 		return;
2523a04a10f8SKris Kennaway 	}
2524a04a10f8SKris Kennaway 	if (s->chanid != -1)
2525a04a10f8SKris Kennaway 		session_exit_message(s, status);
2526b74df5b2SDag-Erling Smørgrav 	if (s->ttyfd != -1)
2527b74df5b2SDag-Erling Smørgrav 		session_pty_cleanup(s);
2528333ee039SDag-Erling Smørgrav 	s->pid = 0;
2529a04a10f8SKris Kennaway }
2530a04a10f8SKris Kennaway 
2531a04a10f8SKris Kennaway /*
2532a04a10f8SKris Kennaway  * this is called when a channel dies before
2533a04a10f8SKris Kennaway  * the session 'child' itself dies
2534a04a10f8SKris Kennaway  */
2535a04a10f8SKris Kennaway void
2536a04a10f8SKris Kennaway session_close_by_channel(int id, void *arg)
2537a04a10f8SKris Kennaway {
2538a04a10f8SKris Kennaway 	Session *s = session_by_channel(id);
2539b74df5b2SDag-Erling Smørgrav 	u_int i;
2540d4ecd108SDag-Erling Smørgrav 
2541a04a10f8SKris Kennaway 	if (s == NULL) {
2542af12a3e7SDag-Erling Smørgrav 		debug("session_close_by_channel: no session for id %d", id);
2543a04a10f8SKris Kennaway 		return;
2544a04a10f8SKris Kennaway 	}
254580628bacSDag-Erling Smørgrav 	debug("session_close_by_channel: channel %d child %ld",
254680628bacSDag-Erling Smørgrav 	    id, (long)s->pid);
2547af12a3e7SDag-Erling Smørgrav 	if (s->pid != 0) {
2548af12a3e7SDag-Erling Smørgrav 		debug("session_close_by_channel: channel %d: has child", id);
2549af12a3e7SDag-Erling Smørgrav 		/*
2550af12a3e7SDag-Erling Smørgrav 		 * delay detach of session, but release pty, since
2551af12a3e7SDag-Erling Smørgrav 		 * the fd's to the child are already closed
2552af12a3e7SDag-Erling Smørgrav 		 */
25531ec0d754SDag-Erling Smørgrav 		if (s->ttyfd != -1)
2554af12a3e7SDag-Erling Smørgrav 			session_pty_cleanup(s);
2555af12a3e7SDag-Erling Smørgrav 		return;
2556af12a3e7SDag-Erling Smørgrav 	}
2557af12a3e7SDag-Erling Smørgrav 	/* detach by removing callback */
2558a04a10f8SKris Kennaway 	channel_cancel_cleanup(s->chanid);
2559b74df5b2SDag-Erling Smørgrav 
2560b74df5b2SDag-Erling Smørgrav 	/* Close any X11 listeners associated with this session */
2561b74df5b2SDag-Erling Smørgrav 	if (s->x11_chanids != NULL) {
2562b74df5b2SDag-Erling Smørgrav 		for (i = 0; s->x11_chanids[i] != -1; i++) {
2563b74df5b2SDag-Erling Smørgrav 			session_close_x11(s->x11_chanids[i]);
2564b74df5b2SDag-Erling Smørgrav 			s->x11_chanids[i] = -1;
2565b74df5b2SDag-Erling Smørgrav 		}
2566b74df5b2SDag-Erling Smørgrav 	}
2567b74df5b2SDag-Erling Smørgrav 
2568a04a10f8SKris Kennaway 	s->chanid = -1;
2569a04a10f8SKris Kennaway 	session_close(s);
2570af12a3e7SDag-Erling Smørgrav }
2571af12a3e7SDag-Erling Smørgrav 
2572af12a3e7SDag-Erling Smørgrav void
257380628bacSDag-Erling Smørgrav session_destroy_all(void (*closefunc)(Session *))
2574af12a3e7SDag-Erling Smørgrav {
2575af12a3e7SDag-Erling Smørgrav 	int i;
2576d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
2577af12a3e7SDag-Erling Smørgrav 		Session *s = &sessions[i];
257880628bacSDag-Erling Smørgrav 		if (s->used) {
257980628bacSDag-Erling Smørgrav 			if (closefunc != NULL)
258080628bacSDag-Erling Smørgrav 				closefunc(s);
258180628bacSDag-Erling Smørgrav 			else
2582af12a3e7SDag-Erling Smørgrav 				session_close(s);
2583a04a10f8SKris Kennaway 		}
2584a04a10f8SKris Kennaway 	}
258580628bacSDag-Erling Smørgrav }
2586a04a10f8SKris Kennaway 
2587af12a3e7SDag-Erling Smørgrav static char *
2588a04a10f8SKris Kennaway session_tty_list(void)
2589a04a10f8SKris Kennaway {
2590a04a10f8SKris Kennaway 	static char buf[1024];
2591a04a10f8SKris Kennaway 	int i;
2592e73e9afaSDag-Erling Smørgrav 	char *cp;
2593e73e9afaSDag-Erling Smørgrav 
2594a04a10f8SKris Kennaway 	buf[0] = '\0';
2595d4af9e69SDag-Erling Smørgrav 	for (i = 0; i < sessions_nalloc; i++) {
2596a04a10f8SKris Kennaway 		Session *s = &sessions[i];
2597a04a10f8SKris Kennaway 		if (s->used && s->ttyfd != -1) {
2598e73e9afaSDag-Erling Smørgrav 
2599e73e9afaSDag-Erling Smørgrav 			if (strncmp(s->tty, "/dev/", 5) != 0) {
2600e73e9afaSDag-Erling Smørgrav 				cp = strrchr(s->tty, '/');
2601e73e9afaSDag-Erling Smørgrav 				cp = (cp == NULL) ? s->tty : cp + 1;
2602e73e9afaSDag-Erling Smørgrav 			} else
2603e73e9afaSDag-Erling Smørgrav 				cp = s->tty + 5;
2604e73e9afaSDag-Erling Smørgrav 
2605a04a10f8SKris Kennaway 			if (buf[0] != '\0')
2606a04a10f8SKris Kennaway 				strlcat(buf, ",", sizeof buf);
2607e73e9afaSDag-Erling Smørgrav 			strlcat(buf, cp, sizeof buf);
2608a04a10f8SKris Kennaway 		}
2609a04a10f8SKris Kennaway 	}
2610a04a10f8SKris Kennaway 	if (buf[0] == '\0')
2611a04a10f8SKris Kennaway 		strlcpy(buf, "notty", sizeof buf);
2612a04a10f8SKris Kennaway 	return buf;
2613a04a10f8SKris Kennaway }
2614a04a10f8SKris Kennaway 
2615a04a10f8SKris Kennaway void
2616a04a10f8SKris Kennaway session_proctitle(Session *s)
2617a04a10f8SKris Kennaway {
2618a04a10f8SKris Kennaway 	if (s->pw == NULL)
2619a04a10f8SKris Kennaway 		error("no user for session %d", s->self);
2620a04a10f8SKris Kennaway 	else
2621a04a10f8SKris Kennaway 		setproctitle("%s@%s", s->pw->pw_name, session_tty_list());
2622a04a10f8SKris Kennaway }
2623a04a10f8SKris Kennaway 
2624af12a3e7SDag-Erling Smørgrav int
2625af12a3e7SDag-Erling Smørgrav session_setup_x11fwd(Session *s)
2626af12a3e7SDag-Erling Smørgrav {
2627af12a3e7SDag-Erling Smørgrav 	struct stat st;
2628af12a3e7SDag-Erling Smørgrav 	char display[512], auth_display[512];
2629af12a3e7SDag-Erling Smørgrav 	char hostname[MAXHOSTNAMELEN];
2630d4ecd108SDag-Erling Smørgrav 	u_int i;
2631af12a3e7SDag-Erling Smørgrav 
2632af12a3e7SDag-Erling Smørgrav 	if (no_x11_forwarding_flag) {
2633af12a3e7SDag-Erling Smørgrav 		packet_send_debug("X11 forwarding disabled in user configuration file.");
2634af12a3e7SDag-Erling Smørgrav 		return 0;
2635af12a3e7SDag-Erling Smørgrav 	}
2636af12a3e7SDag-Erling Smørgrav 	if (!options.x11_forwarding) {
2637af12a3e7SDag-Erling Smørgrav 		debug("X11 forwarding disabled in server configuration file.");
2638af12a3e7SDag-Erling Smørgrav 		return 0;
2639af12a3e7SDag-Erling Smørgrav 	}
2640af12a3e7SDag-Erling Smørgrav 	if (!options.xauth_location ||
2641af12a3e7SDag-Erling Smørgrav 	    (stat(options.xauth_location, &st) == -1)) {
2642af12a3e7SDag-Erling Smørgrav 		packet_send_debug("No xauth program; cannot forward with spoofing.");
2643af12a3e7SDag-Erling Smørgrav 		return 0;
2644af12a3e7SDag-Erling Smørgrav 	}
2645af12a3e7SDag-Erling Smørgrav 	if (options.use_login) {
2646af12a3e7SDag-Erling Smørgrav 		packet_send_debug("X11 forwarding disabled; "
2647af12a3e7SDag-Erling Smørgrav 		    "not compatible with UseLogin=yes.");
2648af12a3e7SDag-Erling Smørgrav 		return 0;
2649af12a3e7SDag-Erling Smørgrav 	}
2650af12a3e7SDag-Erling Smørgrav 	if (s->display != NULL) {
2651af12a3e7SDag-Erling Smørgrav 		debug("X11 display already set.");
2652af12a3e7SDag-Erling Smørgrav 		return 0;
2653af12a3e7SDag-Erling Smørgrav 	}
2654a82e551fSDag-Erling Smørgrav 	if (x11_create_display_inet(options.x11_display_offset,
2655a82e551fSDag-Erling Smørgrav 	    options.x11_use_localhost, s->single_connection,
2656d4ecd108SDag-Erling Smørgrav 	    &s->display_number, &s->x11_chanids) == -1) {
2657af12a3e7SDag-Erling Smørgrav 		debug("x11_create_display_inet failed.");
2658af12a3e7SDag-Erling Smørgrav 		return 0;
2659af12a3e7SDag-Erling Smørgrav 	}
2660d4ecd108SDag-Erling Smørgrav 	for (i = 0; s->x11_chanids[i] != -1; i++) {
2661d4ecd108SDag-Erling Smørgrav 		channel_register_cleanup(s->x11_chanids[i],
2662b74df5b2SDag-Erling Smørgrav 		    session_close_single_x11, 0);
2663d4ecd108SDag-Erling Smørgrav 	}
2664af12a3e7SDag-Erling Smørgrav 
2665af12a3e7SDag-Erling Smørgrav 	/* Set up a suitable value for the DISPLAY variable. */
2666af12a3e7SDag-Erling Smørgrav 	if (gethostname(hostname, sizeof(hostname)) < 0)
2667af12a3e7SDag-Erling Smørgrav 		fatal("gethostname: %.100s", strerror(errno));
2668af12a3e7SDag-Erling Smørgrav 	/*
2669af12a3e7SDag-Erling Smørgrav 	 * auth_display must be used as the displayname when the
2670af12a3e7SDag-Erling Smørgrav 	 * authorization entry is added with xauth(1).  This will be
2671af12a3e7SDag-Erling Smørgrav 	 * different than the DISPLAY string for localhost displays.
2672af12a3e7SDag-Erling Smørgrav 	 */
2673af12a3e7SDag-Erling Smørgrav 	if (options.x11_use_localhost) {
2674a82e551fSDag-Erling Smørgrav 		snprintf(display, sizeof display, "localhost:%u.%u",
2675af12a3e7SDag-Erling Smørgrav 		    s->display_number, s->screen);
2676a82e551fSDag-Erling Smørgrav 		snprintf(auth_display, sizeof auth_display, "unix:%u.%u",
2677af12a3e7SDag-Erling Smørgrav 		    s->display_number, s->screen);
2678af12a3e7SDag-Erling Smørgrav 		s->display = xstrdup(display);
2679af12a3e7SDag-Erling Smørgrav 		s->auth_display = xstrdup(auth_display);
2680af12a3e7SDag-Erling Smørgrav 	} else {
2681989dd127SDag-Erling Smørgrav #ifdef IPADDR_IN_DISPLAY
2682989dd127SDag-Erling Smørgrav 		struct hostent *he;
2683989dd127SDag-Erling Smørgrav 		struct in_addr my_addr;
2684989dd127SDag-Erling Smørgrav 
2685989dd127SDag-Erling Smørgrav 		he = gethostbyname(hostname);
2686989dd127SDag-Erling Smørgrav 		if (he == NULL) {
2687989dd127SDag-Erling Smørgrav 			error("Can't get IP address for X11 DISPLAY.");
2688989dd127SDag-Erling Smørgrav 			packet_send_debug("Can't get IP address for X11 DISPLAY.");
2689989dd127SDag-Erling Smørgrav 			return 0;
2690989dd127SDag-Erling Smørgrav 		}
2691989dd127SDag-Erling Smørgrav 		memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr));
2692a82e551fSDag-Erling Smørgrav 		snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr),
2693989dd127SDag-Erling Smørgrav 		    s->display_number, s->screen);
2694989dd127SDag-Erling Smørgrav #else
2695a82e551fSDag-Erling Smørgrav 		snprintf(display, sizeof display, "%.400s:%u.%u", hostname,
2696af12a3e7SDag-Erling Smørgrav 		    s->display_number, s->screen);
2697989dd127SDag-Erling Smørgrav #endif
2698af12a3e7SDag-Erling Smørgrav 		s->display = xstrdup(display);
2699af12a3e7SDag-Erling Smørgrav 		s->auth_display = xstrdup(display);
2700af12a3e7SDag-Erling Smørgrav 	}
2701af12a3e7SDag-Erling Smørgrav 
2702af12a3e7SDag-Erling Smørgrav 	return 1;
2703af12a3e7SDag-Erling Smørgrav }
2704af12a3e7SDag-Erling Smørgrav 
2705af12a3e7SDag-Erling Smørgrav static void
2706ca3176e7SBrian Feldman do_authenticated2(Authctxt *authctxt)
2707a04a10f8SKris Kennaway {
2708af12a3e7SDag-Erling Smørgrav 	server_loop2(authctxt);
27091ec0d754SDag-Erling Smørgrav }
27101ec0d754SDag-Erling Smørgrav 
27111ec0d754SDag-Erling Smørgrav void
27121ec0d754SDag-Erling Smørgrav do_cleanup(Authctxt *authctxt)
27131ec0d754SDag-Erling Smørgrav {
27141ec0d754SDag-Erling Smørgrav 	static int called = 0;
27151ec0d754SDag-Erling Smørgrav 
27161ec0d754SDag-Erling Smørgrav 	debug("do_cleanup");
27171ec0d754SDag-Erling Smørgrav 
27181ec0d754SDag-Erling Smørgrav 	/* no cleanup if we're in the child for login shell */
27191ec0d754SDag-Erling Smørgrav 	if (is_child)
27201ec0d754SDag-Erling Smørgrav 		return;
27211ec0d754SDag-Erling Smørgrav 
27221ec0d754SDag-Erling Smørgrav 	/* avoid double cleanup */
27231ec0d754SDag-Erling Smørgrav 	if (called)
27241ec0d754SDag-Erling Smørgrav 		return;
27251ec0d754SDag-Erling Smørgrav 	called = 1;
27261ec0d754SDag-Erling Smørgrav 
2727d4af9e69SDag-Erling Smørgrav 	if (authctxt == NULL)
27281ec0d754SDag-Erling Smørgrav 		return;
2729d4af9e69SDag-Erling Smørgrav 
2730d4af9e69SDag-Erling Smørgrav #ifdef USE_PAM
2731d4af9e69SDag-Erling Smørgrav 	if (options.use_pam) {
2732d4af9e69SDag-Erling Smørgrav 		sshpam_cleanup();
2733d4af9e69SDag-Erling Smørgrav 		sshpam_thread_cleanup();
2734d4af9e69SDag-Erling Smørgrav 	}
2735d4af9e69SDag-Erling Smørgrav #endif
2736d4af9e69SDag-Erling Smørgrav 
2737d4af9e69SDag-Erling Smørgrav 	if (!authctxt->authenticated)
2738d4af9e69SDag-Erling Smørgrav 		return;
2739d4af9e69SDag-Erling Smørgrav 
27401ec0d754SDag-Erling Smørgrav #ifdef KRB5
27411ec0d754SDag-Erling Smørgrav 	if (options.kerberos_ticket_cleanup &&
27421ec0d754SDag-Erling Smørgrav 	    authctxt->krb5_ctx)
27431ec0d754SDag-Erling Smørgrav 		krb5_cleanup_proc(authctxt);
2744cf2b5f3bSDag-Erling Smørgrav #endif
27451ec0d754SDag-Erling Smørgrav 
27461ec0d754SDag-Erling Smørgrav #ifdef GSSAPI
27471ec0d754SDag-Erling Smørgrav 	if (compat20 && options.gss_cleanup_creds)
27481ec0d754SDag-Erling Smørgrav 		ssh_gssapi_cleanup_creds();
27491ec0d754SDag-Erling Smørgrav #endif
27501ec0d754SDag-Erling Smørgrav 
27511ec0d754SDag-Erling Smørgrav 	/* remove agent socket */
27521ec0d754SDag-Erling Smørgrav 	auth_sock_cleanup_proc(authctxt->pw);
27531ec0d754SDag-Erling Smørgrav 
27541ec0d754SDag-Erling Smørgrav 	/*
27551ec0d754SDag-Erling Smørgrav 	 * Cleanup ptys/utmp only if privsep is disabled,
27561ec0d754SDag-Erling Smørgrav 	 * or if running in monitor.
27571ec0d754SDag-Erling Smørgrav 	 */
27581ec0d754SDag-Erling Smørgrav 	if (!use_privsep || mm_is_monitor())
27591ec0d754SDag-Erling Smørgrav 		session_destroy_all(session_pty_cleanup2);
2760a04a10f8SKris Kennaway }
2761