xref: /freebsd/crypto/openssh/session.c (revision e9fd63dfddbda8f8a98486fe813eaf17ab3ecf1c)
1a04a10f8SKris Kennaway /*
2a04a10f8SKris Kennaway  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
3a04a10f8SKris Kennaway  *                    All rights reserved
4c2d3a559SKris Kennaway  *
5c2d3a559SKris Kennaway  * As far as I am concerned, the code I have written for this software
6c2d3a559SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
7c2d3a559SKris Kennaway  * software must be clearly marked as such, and if the derived work is
8c2d3a559SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
9c2d3a559SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
10c2d3a559SKris Kennaway  *
11a04a10f8SKris Kennaway  * SSH2 support by Markus Friedl.
12a04a10f8SKris Kennaway  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
13e8aafc91SKris Kennaway  *
14c2d3a559SKris Kennaway  * Redistribution and use in source and binary forms, with or without
15c2d3a559SKris Kennaway  * modification, are permitted provided that the following conditions
16c2d3a559SKris Kennaway  * are met:
17c2d3a559SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
18c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
19c2d3a559SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
20c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
21c2d3a559SKris Kennaway  *    documentation and/or other materials provided with the distribution.
22c2d3a559SKris Kennaway  *
23c2d3a559SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24c2d3a559SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25c2d3a559SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26c2d3a559SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27c2d3a559SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28c2d3a559SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29c2d3a559SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30c2d3a559SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31c2d3a559SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32c2d3a559SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33a04a10f8SKris Kennaway  */
34a04a10f8SKris Kennaway 
35a04a10f8SKris Kennaway #include "includes.h"
36e9fd63dfSBrian Feldman RCSID("$OpenBSD: session.c,v 1.80 2001/06/04 21:59:43 markus Exp $");
37c2d3a559SKris Kennaway RCSID("$FreeBSD$");
38a04a10f8SKris Kennaway 
39a04a10f8SKris Kennaway #include "ssh.h"
40ca3176e7SBrian Feldman #include "ssh1.h"
41ca3176e7SBrian Feldman #include "ssh2.h"
42ca3176e7SBrian Feldman #include "xmalloc.h"
43ca3176e7SBrian Feldman #include "sshpty.h"
44a04a10f8SKris Kennaway #include "packet.h"
45a04a10f8SKris Kennaway #include "buffer.h"
46a04a10f8SKris Kennaway #include "mpaux.h"
47a04a10f8SKris Kennaway #include "uidswap.h"
48a04a10f8SKris Kennaway #include "compat.h"
49a04a10f8SKris Kennaway #include "channels.h"
50a04a10f8SKris Kennaway #include "nchan.h"
51a04a10f8SKris Kennaway #include "bufaux.h"
52a04a10f8SKris Kennaway #include "auth.h"
53c2d3a559SKris Kennaway #include "auth-options.h"
54ca3176e7SBrian Feldman #include "pathnames.h"
55ca3176e7SBrian Feldman #include "log.h"
56ca3176e7SBrian Feldman #include "servconf.h"
57ca3176e7SBrian Feldman #include "sshlogin.h"
58ca3176e7SBrian Feldman #include "serverloop.h"
59ca3176e7SBrian Feldman #include "canohost.h"
60ca3176e7SBrian Feldman #include "session.h"
61a04a10f8SKris Kennaway 
62e8aafc91SKris Kennaway #ifdef __FreeBSD__
63e8aafc91SKris Kennaway #define _PATH_CHPASS "/usr/bin/passwd"
64e8aafc91SKris Kennaway #endif /* __FreeBSD__ */
65e8aafc91SKris Kennaway 
66c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
67e8aafc91SKris Kennaway #include <login_cap.h>
68c2d3a559SKris Kennaway #endif
69e8aafc91SKris Kennaway 
70b787acb5SKris Kennaway #ifdef KRB5
71b787acb5SKris Kennaway extern krb5_context ssh_context;
72b787acb5SKris Kennaway #endif
73b787acb5SKris Kennaway 
74a04a10f8SKris Kennaway /* types */
75a04a10f8SKris Kennaway 
76a04a10f8SKris Kennaway #define TTYSZ 64
77a04a10f8SKris Kennaway typedef struct Session Session;
78a04a10f8SKris Kennaway struct Session {
79a04a10f8SKris Kennaway 	int	used;
80a04a10f8SKris Kennaway 	int	self;
81a04a10f8SKris Kennaway 	struct	passwd *pw;
82a04a10f8SKris Kennaway 	pid_t	pid;
83a04a10f8SKris Kennaway 	/* tty */
84a04a10f8SKris Kennaway 	char	*term;
85a04a10f8SKris Kennaway 	int	ptyfd, ttyfd, ptymaster;
86a04a10f8SKris Kennaway 	int	row, col, xpixel, ypixel;
87a04a10f8SKris Kennaway 	char	tty[TTYSZ];
88a04a10f8SKris Kennaway 	/* X11 */
89a04a10f8SKris Kennaway 	char	*display;
90a04a10f8SKris Kennaway 	int	screen;
91a04a10f8SKris Kennaway 	char	*auth_proto;
92a04a10f8SKris Kennaway 	char	*auth_data;
93a04a10f8SKris Kennaway 	int	single_connection;
94a04a10f8SKris Kennaway 	/* proto 2 */
95a04a10f8SKris Kennaway 	int	chanid;
96ca3176e7SBrian Feldman 	int	is_subsystem;
97a04a10f8SKris Kennaway };
98a04a10f8SKris Kennaway 
99a04a10f8SKris Kennaway /* func */
100a04a10f8SKris Kennaway 
101a04a10f8SKris Kennaway Session *session_new(void);
102a04a10f8SKris Kennaway void	session_set_fds(Session *s, int fdin, int fdout, int fderr);
103a04a10f8SKris Kennaway void	session_pty_cleanup(Session *s);
104a04a10f8SKris Kennaway void	session_proctitle(Session *s);
105ca3176e7SBrian Feldman void	do_exec_pty(Session *s, const char *command);
106ca3176e7SBrian Feldman void	do_exec_no_pty(Session *s, const char *command);
107ca3176e7SBrian Feldman void	do_login(Session *s, const char *command);
108ca3176e7SBrian Feldman void	do_child(Session *s, const char *command);
109ca3176e7SBrian Feldman void	do_motd(void);
110ca3176e7SBrian Feldman int	check_quietlogin(Session *s, const char *command);
111e9fd63dfSBrian Feldman void	xauthfile_cleanup_proc(void *pw);
112a04a10f8SKris Kennaway 
113ca3176e7SBrian Feldman void	do_authenticated1(Authctxt *authctxt);
114ca3176e7SBrian Feldman void	do_authenticated2(Authctxt *authctxt);
115a04a10f8SKris Kennaway 
116a04a10f8SKris Kennaway /* import */
117a04a10f8SKris Kennaway extern ServerOptions options;
118a04a10f8SKris Kennaway extern char *__progname;
119a04a10f8SKris Kennaway extern int log_stderr;
120a04a10f8SKris Kennaway extern int debug_flag;
121ca3176e7SBrian Feldman extern u_int utmp_len;
122c2d3a559SKris Kennaway extern int startup_pipe;
123ca3176e7SBrian Feldman extern void destroy_sensitive_data(void);
124a04a10f8SKris Kennaway 
125a04a10f8SKris Kennaway /* Local Xauthority file. */
126a04a10f8SKris Kennaway static char *xauthfile;
127a04a10f8SKris Kennaway 
128c2d3a559SKris Kennaway /* original command from peer. */
129c2d3a559SKris Kennaway char *original_command = NULL;
130c2d3a559SKris Kennaway 
131a04a10f8SKris Kennaway /* data */
132a04a10f8SKris Kennaway #define MAX_SESSIONS 10
133a04a10f8SKris Kennaway Session	sessions[MAX_SESSIONS];
134a04a10f8SKris Kennaway 
135c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
136c2d3a559SKris Kennaway static login_cap_t *lc;
137c2d3a559SKris Kennaway #endif
138a04a10f8SKris Kennaway 
139ca3176e7SBrian Feldman void
140ca3176e7SBrian Feldman do_authenticated(Authctxt *authctxt)
141ca3176e7SBrian Feldman {
142ca3176e7SBrian Feldman 	/*
143ca3176e7SBrian Feldman 	 * Cancel the alarm we set to limit the time taken for
144ca3176e7SBrian Feldman 	 * authentication.
145ca3176e7SBrian Feldman 	 */
146ca3176e7SBrian Feldman 	alarm(0);
147ca3176e7SBrian Feldman 	if (startup_pipe != -1) {
148ca3176e7SBrian Feldman 		close(startup_pipe);
149ca3176e7SBrian Feldman 		startup_pipe = -1;
150ca3176e7SBrian Feldman 	}
151ca3176e7SBrian Feldman #ifdef HAVE_LOGIN_CAP
152ca3176e7SBrian Feldman 	if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) {
153ca3176e7SBrian Feldman 		error("unable to get login class");
154ca3176e7SBrian Feldman 		return;
155ca3176e7SBrian Feldman 	}
156ca3176e7SBrian Feldman #ifdef BSD_AUTH
157ca3176e7SBrian Feldman 	if (auth_approval(NULL, lc, authctxt->pw->pw_name, "ssh") <= 0) {
158ca3176e7SBrian Feldman 		packet_disconnect("Approval failure for %s",
159ca3176e7SBrian Feldman 		    authctxt->pw->pw_name);
160ca3176e7SBrian Feldman 	}
161ca3176e7SBrian Feldman #endif
162ca3176e7SBrian Feldman #endif
163ca3176e7SBrian Feldman 	/* setup the channel layer */
164ca3176e7SBrian Feldman 	if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
165ca3176e7SBrian Feldman 		channel_permit_all_opens();
166ca3176e7SBrian Feldman 
167ca3176e7SBrian Feldman 	if (compat20)
168ca3176e7SBrian Feldman 		do_authenticated2(authctxt);
169ca3176e7SBrian Feldman 	else
170ca3176e7SBrian Feldman 		do_authenticated1(authctxt);
171e9fd63dfSBrian Feldman 
172e9fd63dfSBrian Feldman 	/* remote user's local Xauthority file and agent socket */
173e9fd63dfSBrian Feldman 	if (xauthfile)
174e9fd63dfSBrian Feldman 		xauthfile_cleanup_proc(authctxt->pw);
175e9fd63dfSBrian Feldman 	if (auth_get_socket_name())
176e9fd63dfSBrian Feldman 		auth_sock_cleanup_proc(authctxt->pw);
177ca3176e7SBrian Feldman }
178ca3176e7SBrian Feldman 
179a04a10f8SKris Kennaway /*
180a04a10f8SKris Kennaway  * Remove local Xauthority file.
181a04a10f8SKris Kennaway  */
182a04a10f8SKris Kennaway void
183e9fd63dfSBrian Feldman xauthfile_cleanup_proc(void *_pw)
184a04a10f8SKris Kennaway {
185e9fd63dfSBrian Feldman 	struct passwd *pw = _pw;
186a04a10f8SKris Kennaway 	char *p;
187e9fd63dfSBrian Feldman 
188e9fd63dfSBrian Feldman 	debug("xauthfile_cleanup_proc called");
189e9fd63dfSBrian Feldman 	if (xauthfile != NULL) {
190e9fd63dfSBrian Feldman 		temporarily_use_uid(pw);
191a04a10f8SKris Kennaway 		unlink(xauthfile);
192a04a10f8SKris Kennaway 		p = strrchr(xauthfile, '/');
193a04a10f8SKris Kennaway 		if (p != NULL) {
194a04a10f8SKris Kennaway 			*p = '\0';
195a04a10f8SKris Kennaway 			rmdir(xauthfile);
196a04a10f8SKris Kennaway 		}
197a04a10f8SKris Kennaway 		xfree(xauthfile);
198a04a10f8SKris Kennaway 		xauthfile = NULL;
199e9fd63dfSBrian Feldman 		restore_uid();
200a04a10f8SKris Kennaway 	}
201a04a10f8SKris Kennaway }
202a04a10f8SKris Kennaway 
203a04a10f8SKris Kennaway /*
204a04a10f8SKris Kennaway  * Function to perform cleanup if we get aborted abnormally (e.g., due to a
205a04a10f8SKris Kennaway  * dropped connection).
206a04a10f8SKris Kennaway  */
207a04a10f8SKris Kennaway void
208a04a10f8SKris Kennaway pty_cleanup_proc(void *session)
209a04a10f8SKris Kennaway {
210a04a10f8SKris Kennaway 	Session *s=session;
211a04a10f8SKris Kennaway 	if (s == NULL)
212a04a10f8SKris Kennaway 		fatal("pty_cleanup_proc: no session");
213a04a10f8SKris Kennaway 	debug("pty_cleanup_proc: %s", s->tty);
214a04a10f8SKris Kennaway 
215a04a10f8SKris Kennaway 	if (s->pid != 0) {
216a04a10f8SKris Kennaway 		/* Record that the user has logged out. */
217a04a10f8SKris Kennaway 		record_logout(s->pid, s->tty);
218a04a10f8SKris Kennaway 	}
219a04a10f8SKris Kennaway 
220a04a10f8SKris Kennaway 	/* Release the pseudo-tty. */
221a04a10f8SKris Kennaway 	pty_release(s->tty);
222a04a10f8SKris Kennaway }
223a04a10f8SKris Kennaway 
224a04a10f8SKris Kennaway /*
225a04a10f8SKris Kennaway  * Prepares for an interactive session.  This is called after the user has
226a04a10f8SKris Kennaway  * been successfully authenticated.  During this message exchange, pseudo
227a04a10f8SKris Kennaway  * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
228a04a10f8SKris Kennaway  * are requested, etc.
229a04a10f8SKris Kennaway  */
230a04a10f8SKris Kennaway void
231ca3176e7SBrian Feldman do_authenticated1(Authctxt *authctxt)
232a04a10f8SKris Kennaway {
233a04a10f8SKris Kennaway 	Session *s;
234a04a10f8SKris Kennaway 	char *command;
235ca3176e7SBrian Feldman 	int success, type, fd, n_bytes, plen, screen_flag, have_pty = 0;
236ca3176e7SBrian Feldman 	int compression_level = 0, enable_compression_after_reply = 0;
237ca3176e7SBrian Feldman 	u_int proto_len, data_len, dlen;
238e9fd63dfSBrian Feldman 	struct stat st;
239a04a10f8SKris Kennaway 
240a04a10f8SKris Kennaway 	s = session_new();
241ca3176e7SBrian Feldman 	s->pw = authctxt->pw;
242c2d3a559SKris Kennaway 
243a04a10f8SKris Kennaway 	/*
244a04a10f8SKris Kennaway 	 * We stay in this loop until the client requests to execute a shell
245a04a10f8SKris Kennaway 	 * or a command.
246a04a10f8SKris Kennaway 	 */
247a04a10f8SKris Kennaway 	for (;;) {
248ca3176e7SBrian Feldman 		success = 0;
249a04a10f8SKris Kennaway 
250a04a10f8SKris Kennaway 		/* Get a packet from the client. */
251a04a10f8SKris Kennaway 		type = packet_read(&plen);
252a04a10f8SKris Kennaway 
253a04a10f8SKris Kennaway 		/* Process the packet. */
254a04a10f8SKris Kennaway 		switch (type) {
255a04a10f8SKris Kennaway 		case SSH_CMSG_REQUEST_COMPRESSION:
256a04a10f8SKris Kennaway 			packet_integrity_check(plen, 4, type);
257a04a10f8SKris Kennaway 			compression_level = packet_get_int();
258a04a10f8SKris Kennaway 			if (compression_level < 1 || compression_level > 9) {
259a04a10f8SKris Kennaway 				packet_send_debug("Received illegal compression level %d.",
260a04a10f8SKris Kennaway 				     compression_level);
261a04a10f8SKris Kennaway 				break;
262a04a10f8SKris Kennaway 			}
263a04a10f8SKris Kennaway 			/* Enable compression after we have responded with SUCCESS. */
264a04a10f8SKris Kennaway 			enable_compression_after_reply = 1;
265a04a10f8SKris Kennaway 			success = 1;
266a04a10f8SKris Kennaway 			break;
267a04a10f8SKris Kennaway 
268a04a10f8SKris Kennaway 		case SSH_CMSG_REQUEST_PTY:
269a04a10f8SKris Kennaway 			if (no_pty_flag) {
270a04a10f8SKris Kennaway 				debug("Allocating a pty not permitted for this authentication.");
271a04a10f8SKris Kennaway 				break;
272a04a10f8SKris Kennaway 			}
273a04a10f8SKris Kennaway 			if (have_pty)
274a04a10f8SKris Kennaway 				packet_disconnect("Protocol error: you already have a pty.");
275a04a10f8SKris Kennaway 
276a04a10f8SKris Kennaway 			debug("Allocating pty.");
277a04a10f8SKris Kennaway 
278a04a10f8SKris Kennaway 			/* Allocate a pty and open it. */
279a04a10f8SKris Kennaway 			if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
280a04a10f8SKris Kennaway 			    sizeof(s->tty))) {
281a04a10f8SKris Kennaway 				error("Failed to allocate pty.");
282a04a10f8SKris Kennaway 				break;
283a04a10f8SKris Kennaway 			}
284a04a10f8SKris Kennaway 			fatal_add_cleanup(pty_cleanup_proc, (void *)s);
285ca3176e7SBrian Feldman 			pty_setowner(s->pw, s->tty);
286a04a10f8SKris Kennaway 
287a04a10f8SKris Kennaway 			/* Get TERM from the packet.  Note that the value may be of arbitrary length. */
288a04a10f8SKris Kennaway 			s->term = packet_get_string(&dlen);
289a04a10f8SKris Kennaway 			packet_integrity_check(dlen, strlen(s->term), type);
290a04a10f8SKris Kennaway 			/* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
291a04a10f8SKris Kennaway 			/* Remaining bytes */
292a04a10f8SKris Kennaway 			n_bytes = plen - (4 + dlen + 4 * 4);
293a04a10f8SKris Kennaway 
294a04a10f8SKris Kennaway 			if (strcmp(s->term, "") == 0) {
295a04a10f8SKris Kennaway 				xfree(s->term);
296a04a10f8SKris Kennaway 				s->term = NULL;
297a04a10f8SKris Kennaway 			}
298a04a10f8SKris Kennaway 			/* Get window size from the packet. */
299a04a10f8SKris Kennaway 			s->row = packet_get_int();
300a04a10f8SKris Kennaway 			s->col = packet_get_int();
301a04a10f8SKris Kennaway 			s->xpixel = packet_get_int();
302a04a10f8SKris Kennaway 			s->ypixel = packet_get_int();
303a04a10f8SKris Kennaway 			pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
304a04a10f8SKris Kennaway 
305a04a10f8SKris Kennaway 			/* Get tty modes from the packet. */
306a04a10f8SKris Kennaway 			tty_parse_modes(s->ttyfd, &n_bytes);
307a04a10f8SKris Kennaway 			packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
308a04a10f8SKris Kennaway 
309a04a10f8SKris Kennaway 			session_proctitle(s);
310a04a10f8SKris Kennaway 
311a04a10f8SKris Kennaway 			/* Indicate that we now have a pty. */
312a04a10f8SKris Kennaway 			success = 1;
313a04a10f8SKris Kennaway 			have_pty = 1;
314a04a10f8SKris Kennaway 			break;
315a04a10f8SKris Kennaway 
316a04a10f8SKris Kennaway 		case SSH_CMSG_X11_REQUEST_FORWARDING:
317a04a10f8SKris Kennaway 			if (!options.x11_forwarding) {
318a04a10f8SKris Kennaway 				packet_send_debug("X11 forwarding disabled in server configuration file.");
319a04a10f8SKris Kennaway 				break;
320a04a10f8SKris Kennaway 			}
321e9fd63dfSBrian Feldman 			if (!options.xauth_location ||
322e9fd63dfSBrian Feldman 			    (stat(options.xauth_location, &st) == -1)) {
323c2d3a559SKris Kennaway 				packet_send_debug("No xauth program; cannot forward with spoofing.");
324c2d3a559SKris Kennaway 				break;
325c2d3a559SKris Kennaway 			}
326a04a10f8SKris Kennaway 			if (no_x11_forwarding_flag) {
327a04a10f8SKris Kennaway 				packet_send_debug("X11 forwarding not permitted for this authentication.");
328a04a10f8SKris Kennaway 				break;
329a04a10f8SKris Kennaway 			}
330a04a10f8SKris Kennaway 			debug("Received request for X11 forwarding with auth spoofing.");
331a04a10f8SKris Kennaway 			if (s->display != NULL)
332a04a10f8SKris Kennaway 				packet_disconnect("Protocol error: X11 display already set.");
333a04a10f8SKris Kennaway 
334a04a10f8SKris Kennaway 			s->auth_proto = packet_get_string(&proto_len);
335a04a10f8SKris Kennaway 			s->auth_data = packet_get_string(&data_len);
336a04a10f8SKris Kennaway 
337ca3176e7SBrian Feldman 			screen_flag = packet_get_protocol_flags() &
338ca3176e7SBrian Feldman 			    SSH_PROTOFLAG_SCREEN_NUMBER;
339ca3176e7SBrian Feldman 			debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag);
340ca3176e7SBrian Feldman 
341ca3176e7SBrian Feldman 			if (packet_remaining() == 4) {
342ca3176e7SBrian Feldman 				if (!screen_flag)
343ca3176e7SBrian Feldman 					debug2("Buggy client: "
344ca3176e7SBrian Feldman 					    "X11 screen flag missing");
345ca3176e7SBrian Feldman 				packet_integrity_check(plen,
346ca3176e7SBrian Feldman 				    4 + proto_len + 4 + data_len + 4, type);
347a04a10f8SKris Kennaway 				s->screen = packet_get_int();
348ca3176e7SBrian Feldman 			} else {
349ca3176e7SBrian Feldman 				packet_integrity_check(plen,
350ca3176e7SBrian Feldman 				    4 + proto_len + 4 + data_len, type);
351a04a10f8SKris Kennaway 				s->screen = 0;
352ca3176e7SBrian Feldman 			}
353a04a10f8SKris Kennaway 			s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
354a04a10f8SKris Kennaway 
355a04a10f8SKris Kennaway 			if (s->display == NULL)
356a04a10f8SKris Kennaway 				break;
357a04a10f8SKris Kennaway 
358a04a10f8SKris Kennaway 			/* Setup to always have a local .Xauthority. */
359a04a10f8SKris Kennaway 			xauthfile = xmalloc(MAXPATHLEN);
360a04a10f8SKris Kennaway 			strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
361ca3176e7SBrian Feldman 			temporarily_use_uid(s->pw);
362a04a10f8SKris Kennaway 			if (mkdtemp(xauthfile) == NULL) {
363a04a10f8SKris Kennaway 				restore_uid();
364a04a10f8SKris Kennaway 				error("private X11 dir: mkdtemp %s failed: %s",
365a04a10f8SKris Kennaway 				    xauthfile, strerror(errno));
366a04a10f8SKris Kennaway 				xfree(xauthfile);
367a04a10f8SKris Kennaway 				xauthfile = NULL;
368a04a10f8SKris Kennaway 				/* XXXX remove listening channels */
369a04a10f8SKris Kennaway 				break;
370a04a10f8SKris Kennaway 			}
371a04a10f8SKris Kennaway 			strlcat(xauthfile, "/cookies", MAXPATHLEN);
372c2d3a559SKris Kennaway 			fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
373c2d3a559SKris Kennaway 			if (fd >= 0)
374c2d3a559SKris Kennaway 				close(fd);
375a04a10f8SKris Kennaway 			restore_uid();
376e9fd63dfSBrian Feldman 			fatal_add_cleanup(xauthfile_cleanup_proc, s->pw);
377a04a10f8SKris Kennaway 			success = 1;
378a04a10f8SKris Kennaway 			break;
379a04a10f8SKris Kennaway 
380a04a10f8SKris Kennaway 		case SSH_CMSG_AGENT_REQUEST_FORWARDING:
381a04a10f8SKris Kennaway 			if (no_agent_forwarding_flag || compat13) {
382a04a10f8SKris Kennaway 				debug("Authentication agent forwarding not permitted for this authentication.");
383a04a10f8SKris Kennaway 				break;
384a04a10f8SKris Kennaway 			}
385a04a10f8SKris Kennaway 			debug("Received authentication agent forwarding request.");
386ca3176e7SBrian Feldman 			success = auth_input_request_forwarding(s->pw);
387a04a10f8SKris Kennaway 			break;
388a04a10f8SKris Kennaway 
389a04a10f8SKris Kennaway 		case SSH_CMSG_PORT_FORWARD_REQUEST:
390a04a10f8SKris Kennaway 			if (no_port_forwarding_flag) {
391a04a10f8SKris Kennaway 				debug("Port forwarding not permitted for this authentication.");
392a04a10f8SKris Kennaway 				break;
393a04a10f8SKris Kennaway 			}
39409958426SBrian Feldman 			if (!options.allow_tcp_forwarding) {
39509958426SBrian Feldman 				debug("Port forwarding not permitted.");
39609958426SBrian Feldman 				break;
39709958426SBrian Feldman 			}
398a04a10f8SKris Kennaway 			debug("Received TCP/IP port forwarding request.");
399ca3176e7SBrian Feldman 			channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports);
400a04a10f8SKris Kennaway 			success = 1;
401a04a10f8SKris Kennaway 			break;
402a04a10f8SKris Kennaway 
403a04a10f8SKris Kennaway 		case SSH_CMSG_MAX_PACKET_SIZE:
404a04a10f8SKris Kennaway 			if (packet_set_maxsize(packet_get_int()) > 0)
405a04a10f8SKris Kennaway 				success = 1;
406a04a10f8SKris Kennaway 			break;
407a04a10f8SKris Kennaway 
408a04a10f8SKris Kennaway 		case SSH_CMSG_EXEC_SHELL:
409a04a10f8SKris Kennaway 		case SSH_CMSG_EXEC_CMD:
410a04a10f8SKris Kennaway 			if (type == SSH_CMSG_EXEC_CMD) {
411a04a10f8SKris Kennaway 				command = packet_get_string(&dlen);
412a04a10f8SKris Kennaway 				debug("Exec command '%.500s'", command);
413a04a10f8SKris Kennaway 				packet_integrity_check(plen, 4 + dlen, type);
414a04a10f8SKris Kennaway 			} else {
415a04a10f8SKris Kennaway 				command = NULL;
416a04a10f8SKris Kennaway 				packet_integrity_check(plen, 0, type);
417a04a10f8SKris Kennaway 			}
418a04a10f8SKris Kennaway 			if (forced_command != NULL) {
419c2d3a559SKris Kennaway 				original_command = command;
420a04a10f8SKris Kennaway 				command = forced_command;
421a04a10f8SKris Kennaway 				debug("Forced command '%.500s'", forced_command);
422a04a10f8SKris Kennaway 			}
423a04a10f8SKris Kennaway 			if (have_pty)
424ca3176e7SBrian Feldman 				do_exec_pty(s, command);
425a04a10f8SKris Kennaway 			else
426ca3176e7SBrian Feldman 				do_exec_no_pty(s, command);
427a04a10f8SKris Kennaway 
428a04a10f8SKris Kennaway 			if (command != NULL)
429a04a10f8SKris Kennaway 				xfree(command);
430a04a10f8SKris Kennaway 			return;
431a04a10f8SKris Kennaway 
432a04a10f8SKris Kennaway 		default:
433a04a10f8SKris Kennaway 			/*
434a04a10f8SKris Kennaway 			 * Any unknown messages in this phase are ignored,
435a04a10f8SKris Kennaway 			 * and a failure message is returned.
436a04a10f8SKris Kennaway 			 */
437a04a10f8SKris Kennaway 			log("Unknown packet type received after authentication: %d", type);
438a04a10f8SKris Kennaway 		}
439a04a10f8SKris Kennaway 		packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
440a04a10f8SKris Kennaway 		packet_send();
441a04a10f8SKris Kennaway 		packet_write_wait();
442a04a10f8SKris Kennaway 
443a04a10f8SKris Kennaway 		/* Enable compression now that we have replied if appropriate. */
444a04a10f8SKris Kennaway 		if (enable_compression_after_reply) {
445a04a10f8SKris Kennaway 			enable_compression_after_reply = 0;
446a04a10f8SKris Kennaway 			packet_start_compression(compression_level);
447a04a10f8SKris Kennaway 		}
448a04a10f8SKris Kennaway 	}
449a04a10f8SKris Kennaway }
450a04a10f8SKris Kennaway 
451a04a10f8SKris Kennaway /*
452a04a10f8SKris Kennaway  * This is called to fork and execute a command when we have no tty.  This
453a04a10f8SKris Kennaway  * will call do_child from the child, and server_loop from the parent after
454a04a10f8SKris Kennaway  * setting up file descriptors and such.
455a04a10f8SKris Kennaway  */
456a04a10f8SKris Kennaway void
457ca3176e7SBrian Feldman do_exec_no_pty(Session *s, const char *command)
458a04a10f8SKris Kennaway {
459a04a10f8SKris Kennaway 	int pid;
460a04a10f8SKris Kennaway 
461a04a10f8SKris Kennaway #ifdef USE_PIPES
462a04a10f8SKris Kennaway 	int pin[2], pout[2], perr[2];
463a04a10f8SKris Kennaway 	/* Allocate pipes for communicating with the program. */
464a04a10f8SKris Kennaway 	if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
465a04a10f8SKris Kennaway 		packet_disconnect("Could not create pipes: %.100s",
466a04a10f8SKris Kennaway 				  strerror(errno));
467a04a10f8SKris Kennaway #else /* USE_PIPES */
468a04a10f8SKris Kennaway 	int inout[2], err[2];
469a04a10f8SKris Kennaway 	/* Uses socket pairs to communicate with the program. */
470a04a10f8SKris Kennaway 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
471a04a10f8SKris Kennaway 	    socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
472a04a10f8SKris Kennaway 		packet_disconnect("Could not create socket pairs: %.100s",
473a04a10f8SKris Kennaway 				  strerror(errno));
474a04a10f8SKris Kennaway #endif /* USE_PIPES */
475a04a10f8SKris Kennaway 	if (s == NULL)
476a04a10f8SKris Kennaway 		fatal("do_exec_no_pty: no session");
477a04a10f8SKris Kennaway 
478a04a10f8SKris Kennaway 	session_proctitle(s);
479a04a10f8SKris Kennaway 
48009958426SBrian Feldman #ifdef USE_PAM
48109958426SBrian Feldman 	do_pam_setcred();
48209958426SBrian Feldman #endif /* USE_PAM */
48309958426SBrian Feldman 
484a04a10f8SKris Kennaway 	/* Fork the child. */
485a04a10f8SKris Kennaway 	if ((pid = fork()) == 0) {
486a04a10f8SKris Kennaway 		/* Child.  Reinitialize the log since the pid has changed. */
487a04a10f8SKris Kennaway 		log_init(__progname, options.log_level, options.log_facility, log_stderr);
488a04a10f8SKris Kennaway 
489a04a10f8SKris Kennaway 		/*
490a04a10f8SKris Kennaway 		 * Create a new session and process group since the 4.4BSD
491a04a10f8SKris Kennaway 		 * setlogin() affects the entire process group.
492a04a10f8SKris Kennaway 		 */
493a04a10f8SKris Kennaway 		if (setsid() < 0)
494a04a10f8SKris Kennaway 			error("setsid failed: %.100s", strerror(errno));
495a04a10f8SKris Kennaway 
496a04a10f8SKris Kennaway #ifdef USE_PIPES
497a04a10f8SKris Kennaway 		/*
498a04a10f8SKris Kennaway 		 * Redirect stdin.  We close the parent side of the socket
499a04a10f8SKris Kennaway 		 * pair, and make the child side the standard input.
500a04a10f8SKris Kennaway 		 */
501a04a10f8SKris Kennaway 		close(pin[1]);
502a04a10f8SKris Kennaway 		if (dup2(pin[0], 0) < 0)
503a04a10f8SKris Kennaway 			perror("dup2 stdin");
504a04a10f8SKris Kennaway 		close(pin[0]);
505a04a10f8SKris Kennaway 
506a04a10f8SKris Kennaway 		/* Redirect stdout. */
507a04a10f8SKris Kennaway 		close(pout[0]);
508a04a10f8SKris Kennaway 		if (dup2(pout[1], 1) < 0)
509a04a10f8SKris Kennaway 			perror("dup2 stdout");
510a04a10f8SKris Kennaway 		close(pout[1]);
511a04a10f8SKris Kennaway 
512a04a10f8SKris Kennaway 		/* Redirect stderr. */
513a04a10f8SKris Kennaway 		close(perr[0]);
514a04a10f8SKris Kennaway 		if (dup2(perr[1], 2) < 0)
515a04a10f8SKris Kennaway 			perror("dup2 stderr");
516a04a10f8SKris Kennaway 		close(perr[1]);
517a04a10f8SKris Kennaway #else /* USE_PIPES */
518a04a10f8SKris Kennaway 		/*
519a04a10f8SKris Kennaway 		 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
520a04a10f8SKris Kennaway 		 * use the same socket, as some programs (particularly rdist)
521a04a10f8SKris Kennaway 		 * seem to depend on it.
522a04a10f8SKris Kennaway 		 */
523a04a10f8SKris Kennaway 		close(inout[1]);
524a04a10f8SKris Kennaway 		close(err[1]);
525a04a10f8SKris Kennaway 		if (dup2(inout[0], 0) < 0)	/* stdin */
526a04a10f8SKris Kennaway 			perror("dup2 stdin");
527a04a10f8SKris Kennaway 		if (dup2(inout[0], 1) < 0)	/* stdout.  Note: same socket as stdin. */
528a04a10f8SKris Kennaway 			perror("dup2 stdout");
529a04a10f8SKris Kennaway 		if (dup2(err[0], 2) < 0)	/* stderr */
530a04a10f8SKris Kennaway 			perror("dup2 stderr");
531a04a10f8SKris Kennaway #endif /* USE_PIPES */
532a04a10f8SKris Kennaway 
533a04a10f8SKris Kennaway 		/* Do processing for the child (exec command etc). */
534ca3176e7SBrian Feldman 		do_child(s, command);
535a04a10f8SKris Kennaway 		/* NOTREACHED */
536a04a10f8SKris Kennaway 	}
537a04a10f8SKris Kennaway 	if (pid < 0)
538a04a10f8SKris Kennaway 		packet_disconnect("fork failed: %.100s", strerror(errno));
539a04a10f8SKris Kennaway 	s->pid = pid;
540ca3176e7SBrian Feldman 	/* Set interactive/non-interactive mode. */
541ca3176e7SBrian Feldman 	packet_set_interactive(s->display != NULL);
542a04a10f8SKris Kennaway #ifdef USE_PIPES
543a04a10f8SKris Kennaway 	/* We are the parent.  Close the child sides of the pipes. */
544a04a10f8SKris Kennaway 	close(pin[0]);
545a04a10f8SKris Kennaway 	close(pout[1]);
546a04a10f8SKris Kennaway 	close(perr[1]);
547a04a10f8SKris Kennaway 
548a04a10f8SKris Kennaway 	if (compat20) {
549ca3176e7SBrian Feldman 		session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]);
550a04a10f8SKris Kennaway 	} else {
551a04a10f8SKris Kennaway 		/* Enter the interactive session. */
552a04a10f8SKris Kennaway 		server_loop(pid, pin[1], pout[0], perr[0]);
553ca3176e7SBrian Feldman 		/* server_loop has closed pin[1], pout[0], and perr[0]. */
554a04a10f8SKris Kennaway 	}
555a04a10f8SKris Kennaway #else /* USE_PIPES */
556a04a10f8SKris Kennaway 	/* We are the parent.  Close the child sides of the socket pairs. */
557a04a10f8SKris Kennaway 	close(inout[0]);
558a04a10f8SKris Kennaway 	close(err[0]);
559a04a10f8SKris Kennaway 
560a04a10f8SKris Kennaway 	/*
561a04a10f8SKris Kennaway 	 * Enter the interactive session.  Note: server_loop must be able to
562a04a10f8SKris Kennaway 	 * handle the case that fdin and fdout are the same.
563a04a10f8SKris Kennaway 	 */
564a04a10f8SKris Kennaway 	if (compat20) {
565ca3176e7SBrian Feldman 		session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]);
566a04a10f8SKris Kennaway 	} else {
567a04a10f8SKris Kennaway 		server_loop(pid, inout[1], inout[1], err[1]);
568a04a10f8SKris Kennaway 		/* server_loop has closed inout[1] and err[1]. */
569a04a10f8SKris Kennaway 	}
570a04a10f8SKris Kennaway #endif /* USE_PIPES */
571a04a10f8SKris Kennaway }
572a04a10f8SKris Kennaway 
573a04a10f8SKris Kennaway /*
574a04a10f8SKris Kennaway  * This is called to fork and execute a command when we have a tty.  This
575a04a10f8SKris Kennaway  * will call do_child from the child, and server_loop from the parent after
576a04a10f8SKris Kennaway  * setting up file descriptors, controlling tty, updating wtmp, utmp,
577a04a10f8SKris Kennaway  * lastlog, and other such operations.
578a04a10f8SKris Kennaway  */
579a04a10f8SKris Kennaway void
580ca3176e7SBrian Feldman do_exec_pty(Session *s, const char *command)
581a04a10f8SKris Kennaway {
582a04a10f8SKris Kennaway 	int fdout, ptyfd, ttyfd, ptymaster;
583a04a10f8SKris Kennaway 	pid_t pid;
584a04a10f8SKris Kennaway 
585a04a10f8SKris Kennaway 	if (s == NULL)
586a04a10f8SKris Kennaway 		fatal("do_exec_pty: no session");
587a04a10f8SKris Kennaway 	ptyfd = s->ptyfd;
588a04a10f8SKris Kennaway 	ttyfd = s->ttyfd;
589a04a10f8SKris Kennaway 
59009958426SBrian Feldman #ifdef USE_PAM
591ca3176e7SBrian Feldman 	do_pam_session(s->pw->pw_name, s->tty);
59209958426SBrian Feldman 	do_pam_setcred();
59309958426SBrian Feldman #endif /* USE_PAM */
59409958426SBrian Feldman 
595a04a10f8SKris Kennaway 	/* Fork the child. */
596a04a10f8SKris Kennaway 	if ((pid = fork()) == 0) {
597c2d3a559SKris Kennaway 		/* Child.  Reinitialize the log because the pid has changed. */
598a04a10f8SKris Kennaway 		log_init(__progname, options.log_level, options.log_facility, log_stderr);
599a04a10f8SKris Kennaway 
600a04a10f8SKris Kennaway 		/* Close the master side of the pseudo tty. */
601a04a10f8SKris Kennaway 		close(ptyfd);
602a04a10f8SKris Kennaway 
603a04a10f8SKris Kennaway 		/* Make the pseudo tty our controlling tty. */
604a04a10f8SKris Kennaway 		pty_make_controlling_tty(&ttyfd, s->tty);
605a04a10f8SKris Kennaway 
606a04a10f8SKris Kennaway 		/* Redirect stdin from the pseudo tty. */
607a04a10f8SKris Kennaway 		if (dup2(ttyfd, fileno(stdin)) < 0)
608a04a10f8SKris Kennaway 			error("dup2 stdin failed: %.100s", strerror(errno));
609a04a10f8SKris Kennaway 
610a04a10f8SKris Kennaway 		/* Redirect stdout to the pseudo tty. */
611a04a10f8SKris Kennaway 		if (dup2(ttyfd, fileno(stdout)) < 0)
612a04a10f8SKris Kennaway 			error("dup2 stdin failed: %.100s", strerror(errno));
613a04a10f8SKris Kennaway 
614a04a10f8SKris Kennaway 		/* Redirect stderr to the pseudo tty. */
615a04a10f8SKris Kennaway 		if (dup2(ttyfd, fileno(stderr)) < 0)
616a04a10f8SKris Kennaway 			error("dup2 stdin failed: %.100s", strerror(errno));
617a04a10f8SKris Kennaway 
618a04a10f8SKris Kennaway 		/* Close the extra descriptor for the pseudo tty. */
619a04a10f8SKris Kennaway 		close(ttyfd);
620a04a10f8SKris Kennaway 
621c2d3a559SKris Kennaway 		/* record login, etc. similar to login(1) */
622ca3176e7SBrian Feldman 		if (!(options.use_login && command == NULL))
623ca3176e7SBrian Feldman 			do_login(s, command);
624e8aafc91SKris Kennaway 
625a04a10f8SKris Kennaway 		/* Do common processing for the child, such as execing the command. */
626ca3176e7SBrian Feldman 		do_child(s, command);
627a04a10f8SKris Kennaway 		/* NOTREACHED */
628a04a10f8SKris Kennaway 	}
629a04a10f8SKris Kennaway 	if (pid < 0)
630a04a10f8SKris Kennaway 		packet_disconnect("fork failed: %.100s", strerror(errno));
631a04a10f8SKris Kennaway 	s->pid = pid;
632a04a10f8SKris Kennaway 
633a04a10f8SKris Kennaway 	/* Parent.  Close the slave side of the pseudo tty. */
634a04a10f8SKris Kennaway 	close(ttyfd);
635a04a10f8SKris Kennaway 
636a04a10f8SKris Kennaway 	/*
637a04a10f8SKris Kennaway 	 * Create another descriptor of the pty master side for use as the
638a04a10f8SKris Kennaway 	 * standard input.  We could use the original descriptor, but this
639a04a10f8SKris Kennaway 	 * simplifies code in server_loop.  The descriptor is bidirectional.
640a04a10f8SKris Kennaway 	 */
641a04a10f8SKris Kennaway 	fdout = dup(ptyfd);
642a04a10f8SKris Kennaway 	if (fdout < 0)
643a04a10f8SKris Kennaway 		packet_disconnect("dup #1 failed: %.100s", strerror(errno));
644a04a10f8SKris Kennaway 
645a04a10f8SKris Kennaway 	/* we keep a reference to the pty master */
646a04a10f8SKris Kennaway 	ptymaster = dup(ptyfd);
647a04a10f8SKris Kennaway 	if (ptymaster < 0)
648a04a10f8SKris Kennaway 		packet_disconnect("dup #2 failed: %.100s", strerror(errno));
649a04a10f8SKris Kennaway 	s->ptymaster = ptymaster;
650a04a10f8SKris Kennaway 
651a04a10f8SKris Kennaway 	/* Enter interactive session. */
652ca3176e7SBrian Feldman 	packet_set_interactive(1);
653a04a10f8SKris Kennaway 	if (compat20) {
654a04a10f8SKris Kennaway 		session_set_fds(s, ptyfd, fdout, -1);
655a04a10f8SKris Kennaway 	} else {
656a04a10f8SKris Kennaway 		server_loop(pid, ptyfd, fdout, -1);
657a04a10f8SKris Kennaway 		/* server_loop _has_ closed ptyfd and fdout. */
658a04a10f8SKris Kennaway 		session_pty_cleanup(s);
659a04a10f8SKris Kennaway 	}
660a04a10f8SKris Kennaway }
661a04a10f8SKris Kennaway 
662c2d3a559SKris Kennaway /* administrative, login(1)-like work */
663ca3176e7SBrian Feldman void
664c2d3a559SKris Kennaway do_login(Session *s, const char *command)
665c2d3a559SKris Kennaway {
666c2d3a559SKris Kennaway 	FILE *f;
667c2d3a559SKris Kennaway 	char *time_string, *newcommand;
668c2d3a559SKris Kennaway 	char buf[256];
669c2d3a559SKris Kennaway 	char hostname[MAXHOSTNAMELEN];
670c2d3a559SKris Kennaway 	socklen_t fromlen;
671c2d3a559SKris Kennaway 	struct sockaddr_storage from;
672c2d3a559SKris Kennaway 	time_t last_login_time;
673c2d3a559SKris Kennaway 	struct passwd * pw = s->pw;
674c2d3a559SKris Kennaway 	pid_t pid = getpid();
675c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
676c2d3a559SKris Kennaway 	char *fname;
677c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
678c2d3a559SKris Kennaway #ifdef __FreeBSD__
679c2d3a559SKris Kennaway #define DEFAULT_WARN  (2L * 7L * 86400L)  /* Two weeks */
680c2d3a559SKris Kennaway 	struct timeval tv;
681c2d3a559SKris Kennaway 	time_t warntime = DEFAULT_WARN;
682c2d3a559SKris Kennaway #endif /* __FreeBSD__ */
683c2d3a559SKris Kennaway 
684c2d3a559SKris Kennaway 	/*
685c2d3a559SKris Kennaway 	 * Get IP address of client. If the connection is not a socket, let
686c2d3a559SKris Kennaway 	 * the address be 0.0.0.0.
687c2d3a559SKris Kennaway 	 */
688c2d3a559SKris Kennaway 	memset(&from, 0, sizeof(from));
689c2d3a559SKris Kennaway 	if (packet_connection_is_on_socket()) {
690c2d3a559SKris Kennaway 		fromlen = sizeof(from);
691c2d3a559SKris Kennaway 		if (getpeername(packet_get_connection_in(),
692c2d3a559SKris Kennaway 		     (struct sockaddr *) & from, &fromlen) < 0) {
693c2d3a559SKris Kennaway 			debug("getpeername: %.100s", strerror(errno));
694c2d3a559SKris Kennaway 			fatal_cleanup();
695c2d3a559SKris Kennaway 		}
696c2d3a559SKris Kennaway 	}
697c2d3a559SKris Kennaway 
698c2d3a559SKris Kennaway 	/* Get the time and hostname when the user last logged in. */
699ca3176e7SBrian Feldman 	if (options.print_lastlog) {
700c2d3a559SKris Kennaway 		hostname[0] = '\0';
701c2d3a559SKris Kennaway 		last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
702c2d3a559SKris Kennaway 		    hostname, sizeof(hostname));
703ca3176e7SBrian Feldman 	}
704c2d3a559SKris Kennaway 
705c2d3a559SKris Kennaway 	/* Record that there was a login on that tty from the remote host. */
706c2d3a559SKris Kennaway 	record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
707ca3176e7SBrian Feldman 	    get_remote_name_or_ip(utmp_len, options.reverse_mapping_check),
708ca3176e7SBrian Feldman 	    (struct sockaddr *)&from);
709c2d3a559SKris Kennaway 
71009958426SBrian Feldman #ifdef USE_PAM
71109958426SBrian Feldman 	/*
71209958426SBrian Feldman 	 * If password change is needed, do it now.
71309958426SBrian Feldman 	 * This needs to occur before the ~/.hushlogin check.
71409958426SBrian Feldman 	 */
71509958426SBrian Feldman 	if (pam_password_change_required()) {
71609958426SBrian Feldman 		print_pam_messages();
71709958426SBrian Feldman 		do_pam_chauthtok();
71809958426SBrian Feldman 	}
71909958426SBrian Feldman #endif
72009958426SBrian Feldman 
72109958426SBrian Feldman #ifdef USE_PAM
722ca3176e7SBrian Feldman 	if (!check_quietlogin(s, command) && !pam_password_change_required())
72309958426SBrian Feldman 		print_pam_messages();
72409958426SBrian Feldman #endif /* USE_PAM */
725c2d3a559SKris Kennaway 
726c2d3a559SKris Kennaway #ifdef __FreeBSD__
727c2d3a559SKris Kennaway 	if (pw->pw_change || pw->pw_expire)
728c2d3a559SKris Kennaway 		(void)gettimeofday(&tv, NULL);
729c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
730c2d3a559SKris Kennaway 	warntime = login_getcaptime(lc, "warnpassword",
731c2d3a559SKris Kennaway 				    DEFAULT_WARN, DEFAULT_WARN);
732c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
733c2d3a559SKris Kennaway 	/*
734c2d3a559SKris Kennaway 	 * If the password change time is set and has passed, give the
735c2d3a559SKris Kennaway 	 * user a password expiry notice and chance to change it.
736c2d3a559SKris Kennaway 	 */
737c2d3a559SKris Kennaway 	if (pw->pw_change != 0) {
738c2d3a559SKris Kennaway 		if (tv.tv_sec >= pw->pw_change) {
739c2d3a559SKris Kennaway 			(void)printf(
740c2d3a559SKris Kennaway 			    "Sorry -- your password has expired.\n");
741c2d3a559SKris Kennaway 			log("%s Password expired - forcing change",
742c2d3a559SKris Kennaway 			    pw->pw_name);
74309958426SBrian Feldman 			if (newcommand != NULL)
74409958426SBrian Feldman 				xfree(newcommand);
74509958426SBrian Feldman 			newcommand = xstrdup(_PATH_CHPASS);
746c2d3a559SKris Kennaway 		} else if (pw->pw_change - tv.tv_sec < warntime &&
747ca3176e7SBrian Feldman 			   !check_quietlogin(s, command))
748c2d3a559SKris Kennaway 			(void)printf(
749c2d3a559SKris Kennaway 			    "Warning: your password expires on %s",
750c2d3a559SKris Kennaway 			     ctime(&pw->pw_change));
751c2d3a559SKris Kennaway 	}
752c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
753c2d3a559SKris Kennaway 	warntime = login_getcaptime(lc, "warnexpire",
754c2d3a559SKris Kennaway 				    DEFAULT_WARN, DEFAULT_WARN);
755c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
75609958426SBrian Feldman #ifndef USE_PAM
757c2d3a559SKris Kennaway 	if (pw->pw_expire) {
758c2d3a559SKris Kennaway 		if (tv.tv_sec >= pw->pw_expire) {
759c2d3a559SKris Kennaway 			(void)printf(
760c2d3a559SKris Kennaway 			    "Sorry -- your account has expired.\n");
761c2d3a559SKris Kennaway 			log(
762c2d3a559SKris Kennaway 	   "LOGIN %.200s REFUSED (EXPIRED) FROM %.200s ON TTY %.200s",
763ca3176e7SBrian Feldman 				pw->pw_name, get_remote_name_or_ip(utmp_len,
764ca3176e7SBrian Feldman 				options.reverse_mapping_check), s->tty);
765c2d3a559SKris Kennaway 			exit(254);
766c2d3a559SKris Kennaway 		} else if (pw->pw_expire - tv.tv_sec < warntime &&
767ca3176e7SBrian Feldman 			   !check_quietlogin(s, command))
768c2d3a559SKris Kennaway 			(void)printf(
769c2d3a559SKris Kennaway 			    "Warning: your account expires on %s",
770c2d3a559SKris Kennaway 			     ctime(&pw->pw_expire));
771c2d3a559SKris Kennaway 	}
77209958426SBrian Feldman #endif /* !USE_PAM */
773c2d3a559SKris Kennaway #endif /* __FreeBSD__ */
774c2d3a559SKris Kennaway 
775c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
776c2d3a559SKris Kennaway 	if (!auth_ttyok(lc, s->tty)) {
777c2d3a559SKris Kennaway 		(void)printf("Permission denied.\n");
778c2d3a559SKris Kennaway 		log(
779c2d3a559SKris Kennaway 	       "LOGIN %.200s REFUSED (TTY) FROM %.200s ON TTY %.200s",
780ca3176e7SBrian Feldman 		    pw->pw_name, get_remote_name_or_ip(utmp_len,
781ca3176e7SBrian Feldman 			options.reverse_mapping_check), s->tty);
782c2d3a559SKris Kennaway 		exit(254);
783c2d3a559SKris Kennaway 	}
784c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
785c2d3a559SKris Kennaway 
786c2d3a559SKris Kennaway 	/*
787c2d3a559SKris Kennaway 	 * If the user has logged in before, display the time of last
788c2d3a559SKris Kennaway 	 * login. However, don't display anything extra if a command
789c2d3a559SKris Kennaway 	 * has been specified (so that ssh can be used to execute
790c2d3a559SKris Kennaway 	 * commands on a remote machine without users knowing they
791c2d3a559SKris Kennaway 	 * are going to another machine). Login(1) will do this for
792c2d3a559SKris Kennaway 	 * us as well, so check if login(1) is used
793c2d3a559SKris Kennaway 	 */
794ca3176e7SBrian Feldman 	if (command == NULL && options.print_lastlog &&
795ca3176e7SBrian Feldman 	    last_login_time != 0 && !check_quietlogin(s, command) &&
796c2d3a559SKris Kennaway 	    !options.use_login) {
797c2d3a559SKris Kennaway 		time_string = ctime(&last_login_time);
798c2d3a559SKris Kennaway 		/* Remove the trailing newline. */
799c2d3a559SKris Kennaway 		if (strchr(time_string, '\n'))
800c2d3a559SKris Kennaway 			*strchr(time_string, '\n') = 0;
80109958426SBrian Feldman 		if (strcmp(hostname, "") == 0)
802c2d3a559SKris Kennaway 			printf("Last login: %s\r\n", time_string);
803c2d3a559SKris Kennaway 		else
804c2d3a559SKris Kennaway 			printf("Last login: %s from %s\r\n", time_string, hostname);
805c2d3a559SKris Kennaway 	}
806c2d3a559SKris Kennaway 
807c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
808ca3176e7SBrian Feldman 	if (command == NULL && !check_quietlogin(s, command) &&
809ca3176e7SBrian Feldman 	    !options.use_login) {
810c2d3a559SKris Kennaway 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
811c2d3a559SKris Kennaway 		if (fname != NULL && (f = fopen(fname, "r")) != NULL) {
812c2d3a559SKris Kennaway 			while (fgets(buf, sizeof(buf), f) != NULL)
813c2d3a559SKris Kennaway 				fputs(buf, stdout);
814c2d3a559SKris Kennaway 				fclose(f);
815c2d3a559SKris Kennaway 		} else
816c2d3a559SKris Kennaway 			(void)printf("%s\n\t%s %s\n",
817c2d3a559SKris Kennaway 		"Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
818c2d3a559SKris Kennaway 		"The Regents of the University of California. ",
819c2d3a559SKris Kennaway 		"All rights reserved.");
820c2d3a559SKris Kennaway 	}
821c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
822c2d3a559SKris Kennaway 
823c2d3a559SKris Kennaway 	/*
824c2d3a559SKris Kennaway 	 * Print /etc/motd unless a command was specified or printing
825c2d3a559SKris Kennaway 	 * it was disabled in server options or login(1) will be
826c2d3a559SKris Kennaway 	 * used.  Note that some machines appear to print it in
827c2d3a559SKris Kennaway 	 * /etc/profile or similar.
828c2d3a559SKris Kennaway 	 */
829ca3176e7SBrian Feldman 	if (command == NULL && !check_quietlogin(s, command) && !options.use_login)
830ca3176e7SBrian Feldman 		do_motd();
831ca3176e7SBrian Feldman }
832ca3176e7SBrian Feldman 
833ca3176e7SBrian Feldman /*
834ca3176e7SBrian Feldman  * Display the message of the day.
835ca3176e7SBrian Feldman  */
836ca3176e7SBrian Feldman void
837ca3176e7SBrian Feldman do_motd(void)
838ca3176e7SBrian Feldman {
839ca3176e7SBrian Feldman 	FILE *f;
840ca3176e7SBrian Feldman 	char buf[256];
841ca3176e7SBrian Feldman 
842ca3176e7SBrian Feldman 	if (options.print_motd) {
843c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
844c2d3a559SKris Kennaway 		f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
845c2d3a559SKris Kennaway 		    "/etc/motd"), "r");
846c2d3a559SKris Kennaway #else /* !HAVE_LOGIN_CAP */
847c2d3a559SKris Kennaway 		f = fopen("/etc/motd", "r");
848c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
849c2d3a559SKris Kennaway 		if (f) {
850c2d3a559SKris Kennaway 			while (fgets(buf, sizeof(buf), f))
851c2d3a559SKris Kennaway 				fputs(buf, stdout);
852c2d3a559SKris Kennaway 			fclose(f);
853c2d3a559SKris Kennaway 		}
854c2d3a559SKris Kennaway 	}
855ca3176e7SBrian Feldman }
856c2d3a559SKris Kennaway 
857ca3176e7SBrian Feldman /*
858ca3176e7SBrian Feldman  * Check for quiet login, either .hushlogin or command given.
859ca3176e7SBrian Feldman  */
860ca3176e7SBrian Feldman int
861ca3176e7SBrian Feldman check_quietlogin(Session *s, const char *command)
862ca3176e7SBrian Feldman {
863ca3176e7SBrian Feldman 	char buf[256];
864ca3176e7SBrian Feldman 	struct passwd * pw = s->pw;
865ca3176e7SBrian Feldman 	struct stat st;
866ca3176e7SBrian Feldman 
867ca3176e7SBrian Feldman 	/* Return 1 if .hushlogin exists or a command given. */
868ca3176e7SBrian Feldman 	if (command != NULL)
869ca3176e7SBrian Feldman 		return 1;
870ca3176e7SBrian Feldman 	snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
871c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
872ca3176e7SBrian Feldman 	if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
873ca3176e7SBrian Feldman 		return 1;
874ca3176e7SBrian Feldman #else
875ca3176e7SBrian Feldman 	if (stat(buf, &st) >= 0)
876ca3176e7SBrian Feldman 		return 1;
877ca3176e7SBrian Feldman #endif
878ca3176e7SBrian Feldman 	return 0;
879c2d3a559SKris Kennaway }
880c2d3a559SKris Kennaway 
881a04a10f8SKris Kennaway /*
882a04a10f8SKris Kennaway  * Sets the value of the given variable in the environment.  If the variable
883a04a10f8SKris Kennaway  * already exists, its value is overriden.
884a04a10f8SKris Kennaway  */
885a04a10f8SKris Kennaway void
886ca3176e7SBrian Feldman child_set_env(char ***envp, u_int *envsizep, const char *name,
887a04a10f8SKris Kennaway 	      const char *value)
888a04a10f8SKris Kennaway {
889ca3176e7SBrian Feldman 	u_int i, namelen;
890a04a10f8SKris Kennaway 	char **env;
891a04a10f8SKris Kennaway 
892a04a10f8SKris Kennaway 	/*
893a04a10f8SKris Kennaway 	 * Find the slot where the value should be stored.  If the variable
894a04a10f8SKris Kennaway 	 * already exists, we reuse the slot; otherwise we append a new slot
895a04a10f8SKris Kennaway 	 * at the end of the array, expanding if necessary.
896a04a10f8SKris Kennaway 	 */
897a04a10f8SKris Kennaway 	env = *envp;
898a04a10f8SKris Kennaway 	namelen = strlen(name);
899a04a10f8SKris Kennaway 	for (i = 0; env[i]; i++)
900a04a10f8SKris Kennaway 		if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
901a04a10f8SKris Kennaway 			break;
902a04a10f8SKris Kennaway 	if (env[i]) {
903a04a10f8SKris Kennaway 		/* Reuse the slot. */
904a04a10f8SKris Kennaway 		xfree(env[i]);
905a04a10f8SKris Kennaway 	} else {
906a04a10f8SKris Kennaway 		/* New variable.  Expand if necessary. */
907a04a10f8SKris Kennaway 		if (i >= (*envsizep) - 1) {
908a04a10f8SKris Kennaway 			(*envsizep) += 50;
909a04a10f8SKris Kennaway 			env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
910a04a10f8SKris Kennaway 		}
911a04a10f8SKris Kennaway 		/* Need to set the NULL pointer at end of array beyond the new slot. */
912a04a10f8SKris Kennaway 		env[i + 1] = NULL;
913a04a10f8SKris Kennaway 	}
914a04a10f8SKris Kennaway 
915a04a10f8SKris Kennaway 	/* Allocate space and format the variable in the appropriate slot. */
916a04a10f8SKris Kennaway 	env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
917a04a10f8SKris Kennaway 	snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
918a04a10f8SKris Kennaway }
919a04a10f8SKris Kennaway 
920a04a10f8SKris Kennaway /*
921a04a10f8SKris Kennaway  * Reads environment variables from the given file and adds/overrides them
922a04a10f8SKris Kennaway  * into the environment.  If the file does not exist, this does nothing.
923a04a10f8SKris Kennaway  * Otherwise, it must consist of empty lines, comments (line starts with '#')
924a04a10f8SKris Kennaway  * and assignments of the form name=value.  No other forms are allowed.
925a04a10f8SKris Kennaway  */
926a04a10f8SKris Kennaway void
927ca3176e7SBrian Feldman read_environment_file(char ***env, u_int *envsize,
928a04a10f8SKris Kennaway 		      const char *filename)
929a04a10f8SKris Kennaway {
930a04a10f8SKris Kennaway 	FILE *f;
931a04a10f8SKris Kennaway 	char buf[4096];
932a04a10f8SKris Kennaway 	char *cp, *value;
933a04a10f8SKris Kennaway 
934a04a10f8SKris Kennaway 	f = fopen(filename, "r");
935a04a10f8SKris Kennaway 	if (!f)
936a04a10f8SKris Kennaway 		return;
937a04a10f8SKris Kennaway 
938a04a10f8SKris Kennaway 	while (fgets(buf, sizeof(buf), f)) {
939a04a10f8SKris Kennaway 		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
940a04a10f8SKris Kennaway 			;
941a04a10f8SKris Kennaway 		if (!*cp || *cp == '#' || *cp == '\n')
942a04a10f8SKris Kennaway 			continue;
943a04a10f8SKris Kennaway 		if (strchr(cp, '\n'))
944a04a10f8SKris Kennaway 			*strchr(cp, '\n') = '\0';
945a04a10f8SKris Kennaway 		value = strchr(cp, '=');
946a04a10f8SKris Kennaway 		if (value == NULL) {
947a04a10f8SKris Kennaway 			fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
948a04a10f8SKris Kennaway 			continue;
949a04a10f8SKris Kennaway 		}
950db1cb46cSKris Kennaway 		/*
951db1cb46cSKris Kennaway 		 * Replace the equals sign by nul, and advance value to
952db1cb46cSKris Kennaway 		 * the value string.
953db1cb46cSKris Kennaway 		 */
954a04a10f8SKris Kennaway 		*value = '\0';
955a04a10f8SKris Kennaway 		value++;
956a04a10f8SKris Kennaway 		child_set_env(env, envsize, cp, value);
957a04a10f8SKris Kennaway 	}
958a04a10f8SKris Kennaway 	fclose(f);
959a04a10f8SKris Kennaway }
960a04a10f8SKris Kennaway 
96109958426SBrian Feldman #ifdef USE_PAM
96209958426SBrian Feldman /*
96309958426SBrian Feldman  * Sets any environment variables which have been specified by PAM
96409958426SBrian Feldman  */
96509958426SBrian Feldman void do_pam_environment(char ***env, int *envsize)
96609958426SBrian Feldman {
96709958426SBrian Feldman 	char *equals, var_name[512], var_val[512];
96809958426SBrian Feldman 	char **pam_env;
96909958426SBrian Feldman 	int i;
97009958426SBrian Feldman 
97109958426SBrian Feldman 	if ((pam_env = fetch_pam_environment()) == NULL)
97209958426SBrian Feldman 		return;
97309958426SBrian Feldman 
97409958426SBrian Feldman 	for(i = 0; pam_env[i] != NULL; i++) {
97509958426SBrian Feldman 		if ((equals = strstr(pam_env[i], "=")) == NULL)
97609958426SBrian Feldman 			continue;
97709958426SBrian Feldman 
97809958426SBrian Feldman 		if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) {
97909958426SBrian Feldman 			memset(var_name, '\0', sizeof(var_name));
98009958426SBrian Feldman 			memset(var_val, '\0', sizeof(var_val));
98109958426SBrian Feldman 
98209958426SBrian Feldman 			strncpy(var_name, pam_env[i], equals - pam_env[i]);
98309958426SBrian Feldman 			strcpy(var_val, equals + 1);
98409958426SBrian Feldman 
98509958426SBrian Feldman 			child_set_env(env, envsize, var_name, var_val);
98609958426SBrian Feldman 		}
98709958426SBrian Feldman 	}
98809958426SBrian Feldman }
98909958426SBrian Feldman #endif /* USE_PAM */
99009958426SBrian Feldman 
99109958426SBrian Feldman 
992a04a10f8SKris Kennaway /*
993a04a10f8SKris Kennaway  * Performs common processing for the child, such as setting up the
994a04a10f8SKris Kennaway  * environment, closing extra file descriptors, setting the user and group
995a04a10f8SKris Kennaway  * ids, and executing the command or shell.
996a04a10f8SKris Kennaway  */
997a04a10f8SKris Kennaway void
998ca3176e7SBrian Feldman do_child(Session *s, const char *command)
999a04a10f8SKris Kennaway {
1000c2d3a559SKris Kennaway 	const char *shell, *hostname = NULL, *cp = NULL;
1001ca3176e7SBrian Feldman 	struct passwd * pw = s->pw;
1002a04a10f8SKris Kennaway 	char buf[256];
1003c2d3a559SKris Kennaway 	char cmd[1024];
1004c2d3a559SKris Kennaway 	FILE *f = NULL;
1005ca3176e7SBrian Feldman 	u_int envsize, i;
1006ca3176e7SBrian Feldman 	char **env;
1007a04a10f8SKris Kennaway 	extern char **environ;
1008a04a10f8SKris Kennaway 	struct stat st;
1009a04a10f8SKris Kennaway 	char *argv[10];
1010ca3176e7SBrian Feldman 	int do_xauth = s->auth_proto != NULL && s->auth_data != NULL;
1011ca3176e7SBrian Feldman 
1012ca3176e7SBrian Feldman 	/* remove hostkey from the child's memory */
1013ca3176e7SBrian Feldman 	destroy_sensitive_data();
1014a04a10f8SKris Kennaway 
101595e2a710SKris Kennaway 	/* login(1) is only called if we execute the login shell */
101695e2a710SKris Kennaway 	if (options.use_login && command != NULL)
101795e2a710SKris Kennaway 		options.use_login = 0;
101895e2a710SKris Kennaway 
101909958426SBrian Feldman #ifndef USE_PAM
1020c2d3a559SKris Kennaway 	if (!options.use_login) {
1021c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
1022ca3176e7SBrian Feldman 	if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid)
1023c2d3a559SKris Kennaway 		f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN,
1024c2d3a559SKris Kennaway 		    _PATH_NOLOGIN), "r");
1025c2d3a559SKris Kennaway #else
1026c2d3a559SKris Kennaway 		if (pw->pw_uid)
1027c2d3a559SKris Kennaway 			f = fopen(_PATH_NOLOGIN, "r");
1028c2d3a559SKris Kennaway #endif
1029a04a10f8SKris Kennaway 		if (f) {
1030a04a10f8SKris Kennaway 			/* /etc/nologin exists.  Print its contents and exit. */
1031a04a10f8SKris Kennaway 			while (fgets(buf, sizeof(buf), f))
1032a04a10f8SKris Kennaway 				fputs(buf, stderr);
1033a04a10f8SKris Kennaway 			fclose(f);
1034a04a10f8SKris Kennaway 			exit(254);
1035a04a10f8SKris Kennaway 		}
1036c2d3a559SKris Kennaway 	}
103709958426SBrian Feldman #endif /* !USE_PAM */
1038c2d3a559SKris Kennaway 	/* Set login name, uid, gid, and groups. */
1039a04a10f8SKris Kennaway 	/* Login(1) does this as well, and it needs uid 0 for the "-h"
1040a04a10f8SKris Kennaway 	   switch, so we let login(1) to this for us. */
1041a04a10f8SKris Kennaway 	if (!options.use_login) {
1042c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
1043e8aafc91SKris Kennaway 		char **tmpenv;
1044e8aafc91SKris Kennaway 
1045e8aafc91SKris Kennaway 		/* Initialize temp environment */
1046e8aafc91SKris Kennaway 		envsize = 64;
1047e8aafc91SKris Kennaway 		env = xmalloc(envsize * sizeof(char *));
1048e8aafc91SKris Kennaway 		env[0] = NULL;
1049e8aafc91SKris Kennaway 
1050e8aafc91SKris Kennaway 		child_set_env(&env, &envsize, "PATH",
1051e8aafc91SKris Kennaway 			      (pw->pw_uid == 0) ?
1052e8aafc91SKris Kennaway 			      _PATH_STDPATH : _PATH_DEFPATH);
1053e8aafc91SKris Kennaway 
1054e8aafc91SKris Kennaway 		snprintf(buf, sizeof buf, "%.200s/%.50s",
1055e8aafc91SKris Kennaway 			 _PATH_MAILDIR, pw->pw_name);
1056e8aafc91SKris Kennaway 		child_set_env(&env, &envsize, "MAIL", buf);
1057e8aafc91SKris Kennaway 
1058e8aafc91SKris Kennaway 		if (getenv("TZ"))
1059e8aafc91SKris Kennaway 			child_set_env(&env, &envsize, "TZ", getenv("TZ"));
1060e8aafc91SKris Kennaway 
1061e8aafc91SKris Kennaway 		/* Save parent environment */
1062e8aafc91SKris Kennaway 		tmpenv = environ;
1063e8aafc91SKris Kennaway 		environ = env;
1064e8aafc91SKris Kennaway 
1065e8aafc91SKris Kennaway 		if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETALL) < 0)
1066e8aafc91SKris Kennaway 			fatal("setusercontext failed: %s", strerror(errno));
1067e8aafc91SKris Kennaway 
1068e8aafc91SKris Kennaway 		/* Restore parent environment */
1069e8aafc91SKris Kennaway 		env = environ;
1070e8aafc91SKris Kennaway 		environ = tmpenv;
1071e8aafc91SKris Kennaway 
1072e8aafc91SKris Kennaway 		for (envsize = 0; env[envsize] != NULL; ++envsize)
1073e8aafc91SKris Kennaway 			;
1074e8aafc91SKris Kennaway 		envsize = (envsize < 100) ? 100 : envsize + 16;
1075e8aafc91SKris Kennaway 		env = xrealloc(env, envsize * sizeof(char *));
1076e8aafc91SKris Kennaway 
1077c2d3a559SKris Kennaway #endif /* !HAVE_LOGIN_CAP */
1078a04a10f8SKris Kennaway 		if (getuid() == 0 || geteuid() == 0) {
1079c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
1080c2d3a559SKris Kennaway 			if (setusercontext(lc, pw, pw->pw_uid,
1081c2d3a559SKris Kennaway 			    (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
1082c2d3a559SKris Kennaway 				perror("unable to set user context");
1083c2d3a559SKris Kennaway 				exit(1);
1084c2d3a559SKris Kennaway 			}
1085c2d3a559SKris Kennaway #else
1086c2d3a559SKris Kennaway 			if (setlogin(pw->pw_name) < 0)
1087c2d3a559SKris Kennaway 				error("setlogin failed: %s", strerror(errno));
1088a04a10f8SKris Kennaway 			if (setgid(pw->pw_gid) < 0) {
1089a04a10f8SKris Kennaway 				perror("setgid");
1090a04a10f8SKris Kennaway 				exit(1);
1091a04a10f8SKris Kennaway 			}
1092a04a10f8SKris Kennaway 			/* Initialize the group list. */
1093a04a10f8SKris Kennaway 			if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
1094a04a10f8SKris Kennaway 				perror("initgroups");
1095a04a10f8SKris Kennaway 				exit(1);
1096a04a10f8SKris Kennaway 			}
1097a04a10f8SKris Kennaway 			endgrent();
1098a04a10f8SKris Kennaway 
1099a04a10f8SKris Kennaway 			/* Permanently switch to the desired uid. */
1100ca3176e7SBrian Feldman 			permanently_set_uid(pw);
1101c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
1102a04a10f8SKris Kennaway 		}
1103a04a10f8SKris Kennaway 		if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
1104c2d3a559SKris Kennaway 			fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
1105a04a10f8SKris Kennaway 	}
1106a04a10f8SKris Kennaway 	/*
1107a04a10f8SKris Kennaway 	 * Get the shell from the password data.  An empty shell field is
1108a04a10f8SKris Kennaway 	 * legal, and means /bin/sh.
1109a04a10f8SKris Kennaway 	 */
1110a04a10f8SKris Kennaway 	shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
1111c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
1112c2d3a559SKris Kennaway 	shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
1113c2d3a559SKris Kennaway #endif
1114a04a10f8SKris Kennaway 
1115a04a10f8SKris Kennaway #ifdef AFS
1116a04a10f8SKris Kennaway 	/* Try to get AFS tokens for the local cell. */
1117a04a10f8SKris Kennaway 	if (k_hasafs()) {
1118a04a10f8SKris Kennaway 		char cell[64];
1119a04a10f8SKris Kennaway 
1120a04a10f8SKris Kennaway 		if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
1121a04a10f8SKris Kennaway 			krb_afslog(cell, 0);
1122a04a10f8SKris Kennaway 
1123a04a10f8SKris Kennaway 		krb_afslog(0, 0);
1124a04a10f8SKris Kennaway 	}
1125a04a10f8SKris Kennaway #endif /* AFS */
1126a04a10f8SKris Kennaway 
1127a04a10f8SKris Kennaway 	/* Initialize the environment. */
1128e8aafc91SKris Kennaway 	if (env == NULL) {
1129a04a10f8SKris Kennaway 		envsize = 100;
1130a04a10f8SKris Kennaway 		env = xmalloc(envsize * sizeof(char *));
1131a04a10f8SKris Kennaway 		env[0] = NULL;
1132e8aafc91SKris Kennaway 	}
1133a04a10f8SKris Kennaway 
1134a04a10f8SKris Kennaway 	if (!options.use_login) {
1135a04a10f8SKris Kennaway 		/* Set basic environment. */
1136a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "USER", pw->pw_name);
1137a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
1138a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "HOME", pw->pw_dir);
1139c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
1140c2d3a559SKris Kennaway 		(void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH);
1141c2d3a559SKris Kennaway 		child_set_env(&env, &envsize, "PATH", getenv("PATH"));
1142c2d3a559SKris Kennaway #else
1143a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
1144c2d3a559SKris Kennaway #endif
1145a04a10f8SKris Kennaway 
1146a04a10f8SKris Kennaway 		snprintf(buf, sizeof buf, "%.200s/%.50s",
1147a04a10f8SKris Kennaway 			 _PATH_MAILDIR, pw->pw_name);
1148a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "MAIL", buf);
1149a04a10f8SKris Kennaway 
1150a04a10f8SKris Kennaway 		/* Normal systems set SHELL by default. */
1151a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "SHELL", shell);
1152a04a10f8SKris Kennaway 	}
1153a04a10f8SKris Kennaway 	if (getenv("TZ"))
1154a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
1155a04a10f8SKris Kennaway 
1156a04a10f8SKris Kennaway 	/* Set custom environment options from RSA authentication. */
1157a04a10f8SKris Kennaway 	while (custom_environment) {
1158a04a10f8SKris Kennaway 		struct envstring *ce = custom_environment;
1159a04a10f8SKris Kennaway 		char *s = ce->s;
1160a04a10f8SKris Kennaway 		int i;
1161a04a10f8SKris Kennaway 		for (i = 0; s[i] != '=' && s[i]; i++);
1162a04a10f8SKris Kennaway 		if (s[i] == '=') {
1163a04a10f8SKris Kennaway 			s[i] = 0;
1164a04a10f8SKris Kennaway 			child_set_env(&env, &envsize, s, s + i + 1);
1165a04a10f8SKris Kennaway 		}
1166a04a10f8SKris Kennaway 		custom_environment = ce->next;
1167a04a10f8SKris Kennaway 		xfree(ce->s);
1168a04a10f8SKris Kennaway 		xfree(ce);
1169a04a10f8SKris Kennaway 	}
1170a04a10f8SKris Kennaway 
1171a04a10f8SKris Kennaway 	snprintf(buf, sizeof buf, "%.50s %d %d",
1172a04a10f8SKris Kennaway 		 get_remote_ipaddr(), get_remote_port(), get_local_port());
1173a04a10f8SKris Kennaway 	child_set_env(&env, &envsize, "SSH_CLIENT", buf);
1174a04a10f8SKris Kennaway 
1175ca3176e7SBrian Feldman 	if (s->ttyfd != -1)
1176ca3176e7SBrian Feldman 		child_set_env(&env, &envsize, "SSH_TTY", s->tty);
1177ca3176e7SBrian Feldman 	if (s->term)
1178ca3176e7SBrian Feldman 		child_set_env(&env, &envsize, "TERM", s->term);
1179ca3176e7SBrian Feldman 	if (s->display)
1180ca3176e7SBrian Feldman 		child_set_env(&env, &envsize, "DISPLAY", s->display);
1181c2d3a559SKris Kennaway 	if (original_command)
1182c2d3a559SKris Kennaway 		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
1183c2d3a559SKris Kennaway 		    original_command);
1184a04a10f8SKris Kennaway 
1185a04a10f8SKris Kennaway #ifdef KRB4
1186a04a10f8SKris Kennaway 	{
1187a04a10f8SKris Kennaway 		extern char *ticket;
1188a04a10f8SKris Kennaway 
1189a04a10f8SKris Kennaway 		if (ticket)
1190a04a10f8SKris Kennaway 			child_set_env(&env, &envsize, "KRBTKFILE", ticket);
1191a04a10f8SKris Kennaway 	}
1192a04a10f8SKris Kennaway #endif /* KRB4 */
1193e8aafc91SKris Kennaway #ifdef KRB5
1194e8aafc91SKris Kennaway {
1195e8aafc91SKris Kennaway 	  extern krb5_ccache mem_ccache;
1196e8aafc91SKris Kennaway 
1197e8aafc91SKris Kennaway 	   if (mem_ccache) {
1198e8aafc91SKris Kennaway 	     krb5_error_code problem;
1199e8aafc91SKris Kennaway 	      krb5_ccache ccache;
1200e8aafc91SKris Kennaway #ifdef AFS
1201e8aafc91SKris Kennaway 	      if (k_hasafs())
1202e8aafc91SKris Kennaway 		krb5_afslog(ssh_context, mem_ccache, NULL, NULL);
1203e8aafc91SKris Kennaway #endif /* AFS */
1204e8aafc91SKris Kennaway 
1205e8aafc91SKris Kennaway 	      problem = krb5_cc_default(ssh_context, &ccache);
1206e8aafc91SKris Kennaway 	      if (problem) {}
1207e8aafc91SKris Kennaway 	      else {
1208e8aafc91SKris Kennaway 		problem = krb5_cc_copy_cache(ssh_context, mem_ccache, ccache);
1209e8aafc91SKris Kennaway 		 if (problem) {}
1210e8aafc91SKris Kennaway 	      }
1211e8aafc91SKris Kennaway 
1212e8aafc91SKris Kennaway 	      krb5_cc_close(ssh_context, ccache);
1213e8aafc91SKris Kennaway 	   }
1214e8aafc91SKris Kennaway 
1215e8aafc91SKris Kennaway 	   krb5_cleanup_proc(NULL);
1216e8aafc91SKris Kennaway 	}
1217e8aafc91SKris Kennaway #endif /* KRB5 */
1218a04a10f8SKris Kennaway 
121909958426SBrian Feldman #ifdef USE_PAM
122009958426SBrian Feldman 	/* Pull in any environment variables that may have been set by PAM. */
122109958426SBrian Feldman 	do_pam_environment(&env, &envsize);
122209958426SBrian Feldman #endif /* USE_PAM */
122309958426SBrian Feldman 
1224a04a10f8SKris Kennaway 	if (xauthfile)
1225a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
1226a04a10f8SKris Kennaway 	if (auth_get_socket_name() != NULL)
1227a04a10f8SKris Kennaway 		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
1228a04a10f8SKris Kennaway 			      auth_get_socket_name());
1229a04a10f8SKris Kennaway 
1230a04a10f8SKris Kennaway 	/* read $HOME/.ssh/environment. */
1231a04a10f8SKris Kennaway 	if (!options.use_login) {
1232db1cb46cSKris Kennaway 		snprintf(buf, sizeof buf, "%.200s/.ssh/environment",
1233db1cb46cSKris Kennaway 		    pw->pw_dir);
1234a04a10f8SKris Kennaway 		read_environment_file(&env, &envsize, buf);
1235a04a10f8SKris Kennaway 	}
1236a04a10f8SKris Kennaway 	if (debug_flag) {
1237a04a10f8SKris Kennaway 		/* dump the environment */
1238a04a10f8SKris Kennaway 		fprintf(stderr, "Environment:\n");
1239a04a10f8SKris Kennaway 		for (i = 0; env[i]; i++)
1240a04a10f8SKris Kennaway 			fprintf(stderr, "  %.200s\n", env[i]);
1241a04a10f8SKris Kennaway 	}
1242c2d3a559SKris Kennaway 	/* we have to stash the hostname before we close our socket. */
1243c2d3a559SKris Kennaway 	if (options.use_login)
1244ca3176e7SBrian Feldman 		hostname = get_remote_name_or_ip(utmp_len,
1245ca3176e7SBrian Feldman 		    options.reverse_mapping_check);
1246a04a10f8SKris Kennaway 	/*
1247a04a10f8SKris Kennaway 	 * Close the connection descriptors; note that this is the child, and
1248a04a10f8SKris Kennaway 	 * the server will still have the socket open, and it is important
1249a04a10f8SKris Kennaway 	 * that we do not shutdown it.  Note that the descriptors cannot be
1250a04a10f8SKris Kennaway 	 * closed before building the environment, as we call
1251a04a10f8SKris Kennaway 	 * get_remote_ipaddr there.
1252a04a10f8SKris Kennaway 	 */
1253a04a10f8SKris Kennaway 	if (packet_get_connection_in() == packet_get_connection_out())
1254a04a10f8SKris Kennaway 		close(packet_get_connection_in());
1255a04a10f8SKris Kennaway 	else {
1256a04a10f8SKris Kennaway 		close(packet_get_connection_in());
1257a04a10f8SKris Kennaway 		close(packet_get_connection_out());
1258a04a10f8SKris Kennaway 	}
1259a04a10f8SKris Kennaway 	/*
1260a04a10f8SKris Kennaway 	 * Close all descriptors related to channels.  They will still remain
1261a04a10f8SKris Kennaway 	 * open in the parent.
1262a04a10f8SKris Kennaway 	 */
1263a04a10f8SKris Kennaway 	/* XXX better use close-on-exec? -markus */
1264a04a10f8SKris Kennaway 	channel_close_all();
1265a04a10f8SKris Kennaway 
1266a04a10f8SKris Kennaway 	/*
1267a04a10f8SKris Kennaway 	 * Close any extra file descriptors.  Note that there may still be
1268a04a10f8SKris Kennaway 	 * descriptors left by system functions.  They will be closed later.
1269a04a10f8SKris Kennaway 	 */
1270a04a10f8SKris Kennaway 	endpwent();
1271a04a10f8SKris Kennaway 
1272a04a10f8SKris Kennaway 	/*
127346c9472cSBrian Feldman 	 * Restore any signal handlers set by sshd previously that should
127446c9472cSBrian Feldman 	 * be restored to their initial state.
127546c9472cSBrian Feldman 	 */
127646c9472cSBrian Feldman 	signal(SIGPIPE, SIG_DFL);
127746c9472cSBrian Feldman 
1278a04a10f8SKris Kennaway 	/* Change current directory to the user\'s home directory. */
1279e8aafc91SKris Kennaway 	if (
1280e8aafc91SKris Kennaway #ifdef __FreeBSD__
1281e8aafc91SKris Kennaway 		!*pw->pw_dir ||
1282e8aafc91SKris Kennaway #endif /* __FreeBSD__ */
1283e8aafc91SKris Kennaway 		chdir(pw->pw_dir) < 0
1284e8aafc91SKris Kennaway 	   ) {
1285c2d3a559SKris Kennaway #ifdef HAVE_LOGIN_CAP
1286e8aafc91SKris Kennaway 		if (login_getcapbool(lc, "requirehome", 0)) {
1287e8aafc91SKris Kennaway 			(void)printf("Home directory not available\n");
1288e8aafc91SKris Kennaway 			log("LOGIN %.200s REFUSED (HOMEDIR) ON TTY %.200s",
1289e8aafc91SKris Kennaway 				pw->pw_name, ttyname);
1290e8aafc91SKris Kennaway 			exit(254);
1291e8aafc91SKris Kennaway 		}
1292c2d3a559SKris Kennaway #endif /* HAVE_LOGIN_CAP */
1293e8aafc91SKris Kennaway #ifdef __FreeBSD__
1294e8aafc91SKris Kennaway 		if (chdir("/") < 0) {
1295e8aafc91SKris Kennaway 			(void)printf("Cannot find root directory\n");
1296e8aafc91SKris Kennaway 			log("LOGIN %.200s REFUSED (ROOTDIR) ON TTY %.200s",
1297e8aafc91SKris Kennaway 				pw->pw_name, ttyname);
1298e8aafc91SKris Kennaway 			exit(254);
1299e8aafc91SKris Kennaway 		}
1300ca3176e7SBrian Feldman 		if (!check_quietlogin(s, command) || *pw->pw_dir)
1301e8aafc91SKris Kennaway 			(void)printf(
1302e8aafc91SKris Kennaway 		       "No home directory.\nLogging in with home = \"/\".\n");
1303e8aafc91SKris Kennaway 
1304e8aafc91SKris Kennaway #else /* !__FreeBSD__ */
1305e8aafc91SKris Kennaway 
1306a04a10f8SKris Kennaway 		fprintf(stderr, "Could not chdir to home directory %s: %s\n",
1307a04a10f8SKris Kennaway 			pw->pw_dir, strerror(errno));
1308e8aafc91SKris Kennaway #endif /* __FreeBSD__ */
1309e8aafc91SKris Kennaway 	}
1310ca3176e7SBrian Feldman 
1311ca3176e7SBrian Feldman 	/*
1312ca3176e7SBrian Feldman 	 * Close any extra open file descriptors so that we don\'t have them
1313ca3176e7SBrian Feldman 	 * hanging around in clients.  Note that we want to do this after
1314ca3176e7SBrian Feldman 	 * initgroups, because at least on Solaris 2.3 it leaves file
1315ca3176e7SBrian Feldman 	 * descriptors open.
1316ca3176e7SBrian Feldman 	 */
1317ca3176e7SBrian Feldman 	for (i = 3; i < getdtablesize(); i++)
1318ca3176e7SBrian Feldman 		close(i);
1319a04a10f8SKris Kennaway 
1320a04a10f8SKris Kennaway 	/*
1321a04a10f8SKris Kennaway 	 * Must take new environment into use so that .ssh/rc, /etc/sshrc and
1322a04a10f8SKris Kennaway 	 * xauth are run in the proper environment.
1323a04a10f8SKris Kennaway 	 */
1324a04a10f8SKris Kennaway 	environ = env;
1325a04a10f8SKris Kennaway 
1326a04a10f8SKris Kennaway 	/*
1327a04a10f8SKris Kennaway 	 * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
1328a04a10f8SKris Kennaway 	 * in this order).
1329a04a10f8SKris Kennaway 	 */
1330a04a10f8SKris Kennaway 	if (!options.use_login) {
1331ca3176e7SBrian Feldman 		/* ignore _PATH_SSH_USER_RC for subsystems */
1332ca3176e7SBrian Feldman 		if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) {
1333e9fd63dfSBrian Feldman 			snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
1334e9fd63dfSBrian Feldman 			    shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
1335a04a10f8SKris Kennaway 			if (debug_flag)
1336e9fd63dfSBrian Feldman 				fprintf(stderr, "Running %s\n", cmd);
1337e9fd63dfSBrian Feldman 			f = popen(cmd, "w");
1338a04a10f8SKris Kennaway 			if (f) {
1339ca3176e7SBrian Feldman 				if (do_xauth)
1340ca3176e7SBrian Feldman 					fprintf(f, "%s %s\n", s->auth_proto,
1341ca3176e7SBrian Feldman 					    s->auth_data);
1342a04a10f8SKris Kennaway 				pclose(f);
1343a04a10f8SKris Kennaway 			} else
1344ca3176e7SBrian Feldman 				fprintf(stderr, "Could not run %s\n",
1345ca3176e7SBrian Feldman 				    _PATH_SSH_USER_RC);
1346ca3176e7SBrian Feldman 		} else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) {
1347a04a10f8SKris Kennaway 			if (debug_flag)
1348ca3176e7SBrian Feldman 				fprintf(stderr, "Running %s %s\n", _PATH_BSHELL,
1349ca3176e7SBrian Feldman 				    _PATH_SSH_SYSTEM_RC);
1350ca3176e7SBrian Feldman 			f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w");
1351a04a10f8SKris Kennaway 			if (f) {
1352ca3176e7SBrian Feldman 				if (do_xauth)
1353ca3176e7SBrian Feldman 					fprintf(f, "%s %s\n", s->auth_proto,
1354ca3176e7SBrian Feldman 					    s->auth_data);
1355a04a10f8SKris Kennaway 				pclose(f);
1356a04a10f8SKris Kennaway 			} else
1357ca3176e7SBrian Feldman 				fprintf(stderr, "Could not run %s\n",
1358ca3176e7SBrian Feldman 				    _PATH_SSH_SYSTEM_RC);
1359ca3176e7SBrian Feldman 		} else if (do_xauth && options.xauth_location != NULL) {
1360a04a10f8SKris Kennaway 			/* Add authority data to .Xauthority if appropriate. */
1361ca3176e7SBrian Feldman 			char *screen = strchr(s->display, ':');
1362ca3176e7SBrian Feldman 
1363db1cb46cSKris Kennaway 			if (debug_flag) {
1364db1cb46cSKris Kennaway 				fprintf(stderr,
1365ca3176e7SBrian Feldman 				    "Running %.100s add "
1366ca3176e7SBrian Feldman 				    "%.100s %.100s %.100s\n",
1367ca3176e7SBrian Feldman 				    options.xauth_location, s->display,
1368ca3176e7SBrian Feldman 				    s->auth_proto, s->auth_data);
1369db1cb46cSKris Kennaway 				if (screen != NULL)
1370db1cb46cSKris Kennaway 					fprintf(stderr,
1371db1cb46cSKris Kennaway 					    "Adding %.*s/unix%s %s %s\n",
1372ca3176e7SBrian Feldman 					    (int)(screen - s->display),
1373ca3176e7SBrian Feldman 					    s->display, screen,
1374ca3176e7SBrian Feldman 					    s->auth_proto, s->auth_data);
1375db1cb46cSKris Kennaway 			}
1376c2d3a559SKris Kennaway 			snprintf(cmd, sizeof cmd, "%s -q -",
1377c2d3a559SKris Kennaway 			    options.xauth_location);
1378c2d3a559SKris Kennaway 			f = popen(cmd, "w");
1379a04a10f8SKris Kennaway 			if (f) {
1380ca3176e7SBrian Feldman 				fprintf(f, "add %s %s %s\n", s->display,
1381ca3176e7SBrian Feldman 				    s->auth_proto, s->auth_data);
1382db1cb46cSKris Kennaway 				if (screen != NULL)
1383db1cb46cSKris Kennaway 					fprintf(f, "add %.*s/unix%s %s %s\n",
1384ca3176e7SBrian Feldman 					    (int)(screen - s->display),
1385ca3176e7SBrian Feldman 					    s->display, screen,
1386ca3176e7SBrian Feldman 					    s->auth_proto,
1387ca3176e7SBrian Feldman 					    s->auth_data);
1388a04a10f8SKris Kennaway 				pclose(f);
1389c2d3a559SKris Kennaway 			} else {
1390c2d3a559SKris Kennaway 				fprintf(stderr, "Could not run %s\n",
1391c2d3a559SKris Kennaway 				    cmd);
1392a04a10f8SKris Kennaway 			}
1393a04a10f8SKris Kennaway 		}
1394a04a10f8SKris Kennaway 		/* Get the last component of the shell name. */
1395a04a10f8SKris Kennaway 		cp = strrchr(shell, '/');
1396a04a10f8SKris Kennaway 		if (cp)
1397a04a10f8SKris Kennaway 			cp++;
1398a04a10f8SKris Kennaway 		else
1399a04a10f8SKris Kennaway 			cp = shell;
1400a04a10f8SKris Kennaway 	}
1401ca3176e7SBrian Feldman 
1402ca3176e7SBrian Feldman 	/* restore SIGPIPE for child */
1403ca3176e7SBrian Feldman 	signal(SIGPIPE,  SIG_DFL);
1404ca3176e7SBrian Feldman 
1405a04a10f8SKris Kennaway 	/*
1406a04a10f8SKris Kennaway 	 * If we have no command, execute the shell.  In this case, the shell
1407a04a10f8SKris Kennaway 	 * name to be passed in argv[0] is preceded by '-' to indicate that
1408a04a10f8SKris Kennaway 	 * this is a login shell.
1409a04a10f8SKris Kennaway 	 */
1410a04a10f8SKris Kennaway 	if (!command) {
1411a04a10f8SKris Kennaway 		if (!options.use_login) {
1412a04a10f8SKris Kennaway 			char buf[256];
1413a04a10f8SKris Kennaway 
1414a04a10f8SKris Kennaway 			/*
1415a04a10f8SKris Kennaway 			 * Check for mail if we have a tty and it was enabled
1416a04a10f8SKris Kennaway 			 * in server options.
1417a04a10f8SKris Kennaway 			 */
1418ca3176e7SBrian Feldman 			if (s->ttyfd != -1 && options.check_mail) {
1419a04a10f8SKris Kennaway 				char *mailbox;
1420a04a10f8SKris Kennaway 				struct stat mailstat;
1421ca3176e7SBrian Feldman 
1422a04a10f8SKris Kennaway 				mailbox = getenv("MAIL");
1423a04a10f8SKris Kennaway 				if (mailbox != NULL) {
1424db1cb46cSKris Kennaway 					if (stat(mailbox, &mailstat) != 0 ||
1425db1cb46cSKris Kennaway 					    mailstat.st_size == 0)
1426e8aafc91SKris Kennaway #ifdef __FreeBSD__
1427e8aafc91SKris Kennaway 						;
1428e8aafc91SKris Kennaway #else /* !__FreeBSD__ */
1429a04a10f8SKris Kennaway 						printf("No mail.\n");
1430e8aafc91SKris Kennaway #endif /* __FreeBSD__ */
1431a04a10f8SKris Kennaway 					else if (mailstat.st_mtime < mailstat.st_atime)
1432a04a10f8SKris Kennaway 						printf("You have mail.\n");
1433a04a10f8SKris Kennaway 					else
1434a04a10f8SKris Kennaway 						printf("You have new mail.\n");
1435a04a10f8SKris Kennaway 				}
1436a04a10f8SKris Kennaway 			}
1437a04a10f8SKris Kennaway 			/* Start the shell.  Set initial character to '-'. */
1438a04a10f8SKris Kennaway 			buf[0] = '-';
1439a04a10f8SKris Kennaway 			strncpy(buf + 1, cp, sizeof(buf) - 1);
1440a04a10f8SKris Kennaway 			buf[sizeof(buf) - 1] = 0;
1441a04a10f8SKris Kennaway 
1442a04a10f8SKris Kennaway 			/* Execute the shell. */
1443a04a10f8SKris Kennaway 			argv[0] = buf;
1444a04a10f8SKris Kennaway 			argv[1] = NULL;
1445a04a10f8SKris Kennaway 			execve(shell, argv, env);
1446a04a10f8SKris Kennaway 
1447a04a10f8SKris Kennaway 			/* Executing the shell failed. */
1448a04a10f8SKris Kennaway 			perror(shell);
1449a04a10f8SKris Kennaway 			exit(1);
1450a04a10f8SKris Kennaway 
1451a04a10f8SKris Kennaway 		} else {
1452a04a10f8SKris Kennaway 			/* Launch login(1). */
1453a04a10f8SKris Kennaway 
1454c2d3a559SKris Kennaway 			execl("/usr/bin/login", "login", "-h", hostname,
1455a04a10f8SKris Kennaway 			     "-p", "-f", "--", pw->pw_name, NULL);
1456a04a10f8SKris Kennaway 
1457a04a10f8SKris Kennaway 			/* Login couldn't be executed, die. */
1458a04a10f8SKris Kennaway 
1459a04a10f8SKris Kennaway 			perror("login");
1460a04a10f8SKris Kennaway 			exit(1);
1461a04a10f8SKris Kennaway 		}
1462a04a10f8SKris Kennaway 	}
1463a04a10f8SKris Kennaway 	/*
1464a04a10f8SKris Kennaway 	 * Execute the command using the user's shell.  This uses the -c
1465a04a10f8SKris Kennaway 	 * option to execute the command.
1466a04a10f8SKris Kennaway 	 */
1467a04a10f8SKris Kennaway 	argv[0] = (char *) cp;
1468a04a10f8SKris Kennaway 	argv[1] = "-c";
1469a04a10f8SKris Kennaway 	argv[2] = (char *) command;
1470a04a10f8SKris Kennaway 	argv[3] = NULL;
1471a04a10f8SKris Kennaway 	execve(shell, argv, env);
1472a04a10f8SKris Kennaway 	perror(shell);
1473a04a10f8SKris Kennaway 	exit(1);
1474a04a10f8SKris Kennaway }
1475a04a10f8SKris Kennaway 
1476a04a10f8SKris Kennaway Session *
1477a04a10f8SKris Kennaway session_new(void)
1478a04a10f8SKris Kennaway {
1479a04a10f8SKris Kennaway 	int i;
1480a04a10f8SKris Kennaway 	static int did_init = 0;
1481a04a10f8SKris Kennaway 	if (!did_init) {
1482a04a10f8SKris Kennaway 		debug("session_new: init");
1483a04a10f8SKris Kennaway 		for(i = 0; i < MAX_SESSIONS; i++) {
1484a04a10f8SKris Kennaway 			sessions[i].used = 0;
1485a04a10f8SKris Kennaway 			sessions[i].self = i;
1486a04a10f8SKris Kennaway 		}
1487a04a10f8SKris Kennaway 		did_init = 1;
1488a04a10f8SKris Kennaway 	}
1489a04a10f8SKris Kennaway 	for(i = 0; i < MAX_SESSIONS; i++) {
1490a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1491a04a10f8SKris Kennaway 		if (! s->used) {
1492ca3176e7SBrian Feldman 			memset(s, 0, sizeof(*s));
1493a04a10f8SKris Kennaway 			s->chanid = -1;
1494a04a10f8SKris Kennaway 			s->ptyfd = -1;
1495a04a10f8SKris Kennaway 			s->ttyfd = -1;
1496a04a10f8SKris Kennaway 			s->used = 1;
1497a04a10f8SKris Kennaway 			debug("session_new: session %d", i);
1498a04a10f8SKris Kennaway 			return s;
1499a04a10f8SKris Kennaway 		}
1500a04a10f8SKris Kennaway 	}
1501a04a10f8SKris Kennaway 	return NULL;
1502a04a10f8SKris Kennaway }
1503a04a10f8SKris Kennaway 
1504a04a10f8SKris Kennaway void
1505a04a10f8SKris Kennaway session_dump(void)
1506a04a10f8SKris Kennaway {
1507a04a10f8SKris Kennaway 	int i;
1508a04a10f8SKris Kennaway 	for(i = 0; i < MAX_SESSIONS; i++) {
1509a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1510a04a10f8SKris Kennaway 		debug("dump: used %d session %d %p channel %d pid %d",
1511a04a10f8SKris Kennaway 		    s->used,
1512a04a10f8SKris Kennaway 		    s->self,
1513a04a10f8SKris Kennaway 		    s,
1514a04a10f8SKris Kennaway 		    s->chanid,
1515a04a10f8SKris Kennaway 		    s->pid);
1516a04a10f8SKris Kennaway 	}
1517a04a10f8SKris Kennaway }
1518a04a10f8SKris Kennaway 
1519a04a10f8SKris Kennaway int
1520a04a10f8SKris Kennaway session_open(int chanid)
1521a04a10f8SKris Kennaway {
1522a04a10f8SKris Kennaway 	Session *s = session_new();
1523a04a10f8SKris Kennaway 	debug("session_open: channel %d", chanid);
1524a04a10f8SKris Kennaway 	if (s == NULL) {
1525a04a10f8SKris Kennaway 		error("no more sessions");
1526a04a10f8SKris Kennaway 		return 0;
1527a04a10f8SKris Kennaway 	}
1528a04a10f8SKris Kennaway 	s->pw = auth_get_user();
1529a04a10f8SKris Kennaway 	if (s->pw == NULL)
1530ca3176e7SBrian Feldman 		fatal("no user for session %d", s->self);
1531a04a10f8SKris Kennaway 	debug("session_open: session %d: link with channel %d", s->self, chanid);
1532a04a10f8SKris Kennaway 	s->chanid = chanid;
1533a04a10f8SKris Kennaway 	return 1;
1534a04a10f8SKris Kennaway }
1535a04a10f8SKris Kennaway 
1536a04a10f8SKris Kennaway Session *
1537a04a10f8SKris Kennaway session_by_channel(int id)
1538a04a10f8SKris Kennaway {
1539a04a10f8SKris Kennaway 	int i;
1540a04a10f8SKris Kennaway 	for(i = 0; i < MAX_SESSIONS; i++) {
1541a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1542a04a10f8SKris Kennaway 		if (s->used && s->chanid == id) {
1543a04a10f8SKris Kennaway 			debug("session_by_channel: session %d channel %d", i, id);
1544a04a10f8SKris Kennaway 			return s;
1545a04a10f8SKris Kennaway 		}
1546a04a10f8SKris Kennaway 	}
1547a04a10f8SKris Kennaway 	debug("session_by_channel: unknown channel %d", id);
1548a04a10f8SKris Kennaway 	session_dump();
1549a04a10f8SKris Kennaway 	return NULL;
1550a04a10f8SKris Kennaway }
1551a04a10f8SKris Kennaway 
1552a04a10f8SKris Kennaway Session *
1553a04a10f8SKris Kennaway session_by_pid(pid_t pid)
1554a04a10f8SKris Kennaway {
1555a04a10f8SKris Kennaway 	int i;
1556a04a10f8SKris Kennaway 	debug("session_by_pid: pid %d", pid);
1557a04a10f8SKris Kennaway 	for(i = 0; i < MAX_SESSIONS; i++) {
1558a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1559a04a10f8SKris Kennaway 		if (s->used && s->pid == pid)
1560a04a10f8SKris Kennaway 			return s;
1561a04a10f8SKris Kennaway 	}
1562a04a10f8SKris Kennaway 	error("session_by_pid: unknown pid %d", pid);
1563a04a10f8SKris Kennaway 	session_dump();
1564a04a10f8SKris Kennaway 	return NULL;
1565a04a10f8SKris Kennaway }
1566a04a10f8SKris Kennaway 
1567a04a10f8SKris Kennaway int
1568a04a10f8SKris Kennaway session_window_change_req(Session *s)
1569a04a10f8SKris Kennaway {
1570a04a10f8SKris Kennaway 	s->col = packet_get_int();
1571a04a10f8SKris Kennaway 	s->row = packet_get_int();
1572a04a10f8SKris Kennaway 	s->xpixel = packet_get_int();
1573a04a10f8SKris Kennaway 	s->ypixel = packet_get_int();
1574a04a10f8SKris Kennaway 	packet_done();
1575a04a10f8SKris Kennaway 	pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
1576a04a10f8SKris Kennaway 	return 1;
1577a04a10f8SKris Kennaway }
1578a04a10f8SKris Kennaway 
1579a04a10f8SKris Kennaway int
1580a04a10f8SKris Kennaway session_pty_req(Session *s)
1581a04a10f8SKris Kennaway {
1582ca3176e7SBrian Feldman 	u_int len;
1583ca3176e7SBrian Feldman 	int n_bytes;
1584a04a10f8SKris Kennaway 
1585c2d3a559SKris Kennaway 	if (no_pty_flag)
1586c2d3a559SKris Kennaway 		return 0;
1587a04a10f8SKris Kennaway 	if (s->ttyfd != -1)
1588a04a10f8SKris Kennaway 		return 0;
1589a04a10f8SKris Kennaway 	s->term = packet_get_string(&len);
1590a04a10f8SKris Kennaway 	s->col = packet_get_int();
1591a04a10f8SKris Kennaway 	s->row = packet_get_int();
1592a04a10f8SKris Kennaway 	s->xpixel = packet_get_int();
1593a04a10f8SKris Kennaway 	s->ypixel = packet_get_int();
1594a04a10f8SKris Kennaway 
1595a04a10f8SKris Kennaway 	if (strcmp(s->term, "") == 0) {
1596a04a10f8SKris Kennaway 		xfree(s->term);
1597a04a10f8SKris Kennaway 		s->term = NULL;
1598a04a10f8SKris Kennaway 	}
1599a04a10f8SKris Kennaway 	/* Allocate a pty and open it. */
1600a04a10f8SKris Kennaway 	if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
1601a04a10f8SKris Kennaway 		xfree(s->term);
1602a04a10f8SKris Kennaway 		s->term = NULL;
1603a04a10f8SKris Kennaway 		s->ptyfd = -1;
1604a04a10f8SKris Kennaway 		s->ttyfd = -1;
1605a04a10f8SKris Kennaway 		error("session_pty_req: session %d alloc failed", s->self);
1606a04a10f8SKris Kennaway 		return 0;
1607a04a10f8SKris Kennaway 	}
1608a04a10f8SKris Kennaway 	debug("session_pty_req: session %d alloc %s", s->self, s->tty);
1609a04a10f8SKris Kennaway 	/*
1610a04a10f8SKris Kennaway 	 * Add a cleanup function to clear the utmp entry and record logout
1611a04a10f8SKris Kennaway 	 * time in case we call fatal() (e.g., the connection gets closed).
1612a04a10f8SKris Kennaway 	 */
1613a04a10f8SKris Kennaway 	fatal_add_cleanup(pty_cleanup_proc, (void *)s);
1614a04a10f8SKris Kennaway 	pty_setowner(s->pw, s->tty);
1615a04a10f8SKris Kennaway 	/* Get window size from the packet. */
1616a04a10f8SKris Kennaway 	pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
1617a04a10f8SKris Kennaway 
1618ca3176e7SBrian Feldman 	/* Get tty modes from the packet. */
1619ca3176e7SBrian Feldman 	tty_parse_modes(s->ttyfd, &n_bytes);
1620ca3176e7SBrian Feldman 	packet_done();
1621ca3176e7SBrian Feldman 
1622a04a10f8SKris Kennaway 	session_proctitle(s);
1623a04a10f8SKris Kennaway 
1624a04a10f8SKris Kennaway 	return 1;
1625a04a10f8SKris Kennaway }
1626a04a10f8SKris Kennaway 
1627a04a10f8SKris Kennaway int
1628a04a10f8SKris Kennaway session_subsystem_req(Session *s)
1629a04a10f8SKris Kennaway {
1630ca3176e7SBrian Feldman 	u_int len;
1631a04a10f8SKris Kennaway 	int success = 0;
1632a04a10f8SKris Kennaway 	char *subsys = packet_get_string(&len);
1633c2d3a559SKris Kennaway 	int i;
1634a04a10f8SKris Kennaway 
1635a04a10f8SKris Kennaway 	packet_done();
1636a04a10f8SKris Kennaway 	log("subsystem request for %s", subsys);
1637a04a10f8SKris Kennaway 
1638c2d3a559SKris Kennaway 	for (i = 0; i < options.num_subsystems; i++) {
1639c2d3a559SKris Kennaway 		if(strcmp(subsys, options.subsystem_name[i]) == 0) {
1640c2d3a559SKris Kennaway 			debug("subsystem: exec() %s", options.subsystem_command[i]);
1641ca3176e7SBrian Feldman 			s->is_subsystem = 1;
1642ca3176e7SBrian Feldman 			do_exec_no_pty(s, options.subsystem_command[i]);
1643c2d3a559SKris Kennaway 			success = 1;
1644c2d3a559SKris Kennaway 		}
1645c2d3a559SKris Kennaway 	}
1646c2d3a559SKris Kennaway 
1647c2d3a559SKris Kennaway 	if (!success)
1648c2d3a559SKris Kennaway 		log("subsystem request for %s failed, subsystem not found", subsys);
1649c2d3a559SKris Kennaway 
1650a04a10f8SKris Kennaway 	xfree(subsys);
1651a04a10f8SKris Kennaway 	return success;
1652a04a10f8SKris Kennaway }
1653a04a10f8SKris Kennaway 
1654a04a10f8SKris Kennaway int
1655a04a10f8SKris Kennaway session_x11_req(Session *s)
1656a04a10f8SKris Kennaway {
1657c2d3a559SKris Kennaway 	int fd;
1658e9fd63dfSBrian Feldman 	struct stat st;
1659c2d3a559SKris Kennaway 	if (no_x11_forwarding_flag) {
1660c2d3a559SKris Kennaway 		debug("X11 forwarding disabled in user configuration file.");
1661c2d3a559SKris Kennaway 		return 0;
1662c2d3a559SKris Kennaway 	}
1663a04a10f8SKris Kennaway 	if (!options.x11_forwarding) {
1664a04a10f8SKris Kennaway 		debug("X11 forwarding disabled in server configuration file.");
1665a04a10f8SKris Kennaway 		return 0;
1666a04a10f8SKris Kennaway 	}
1667e9fd63dfSBrian Feldman 	if (!options.xauth_location ||
1668e9fd63dfSBrian Feldman 	    (stat(options.xauth_location, &st) == -1)) {
1669e9fd63dfSBrian Feldman 		packet_send_debug("No xauth program; cannot forward with spoofing.");
1670e9fd63dfSBrian Feldman 		return 0;
1671e9fd63dfSBrian Feldman 	}
1672a04a10f8SKris Kennaway 	if (xauthfile != NULL) {
1673a04a10f8SKris Kennaway 		debug("X11 fwd already started.");
1674a04a10f8SKris Kennaway 		return 0;
1675a04a10f8SKris Kennaway 	}
1676a04a10f8SKris Kennaway 
1677a04a10f8SKris Kennaway 	debug("Received request for X11 forwarding with auth spoofing.");
1678a04a10f8SKris Kennaway 	if (s->display != NULL)
1679a04a10f8SKris Kennaway 		packet_disconnect("Protocol error: X11 display already set.");
1680a04a10f8SKris Kennaway 
1681a04a10f8SKris Kennaway 	s->single_connection = packet_get_char();
1682a04a10f8SKris Kennaway 	s->auth_proto = packet_get_string(NULL);
1683a04a10f8SKris Kennaway 	s->auth_data = packet_get_string(NULL);
1684a04a10f8SKris Kennaway 	s->screen = packet_get_int();
1685a04a10f8SKris Kennaway 	packet_done();
1686a04a10f8SKris Kennaway 
1687a04a10f8SKris Kennaway 	s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
1688a04a10f8SKris Kennaway 	if (s->display == NULL) {
1689a04a10f8SKris Kennaway 		xfree(s->auth_proto);
1690a04a10f8SKris Kennaway 		xfree(s->auth_data);
1691a04a10f8SKris Kennaway 		return 0;
1692a04a10f8SKris Kennaway 	}
1693a04a10f8SKris Kennaway 	xauthfile = xmalloc(MAXPATHLEN);
1694a04a10f8SKris Kennaway 	strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
1695ca3176e7SBrian Feldman 	temporarily_use_uid(s->pw);
1696a04a10f8SKris Kennaway 	if (mkdtemp(xauthfile) == NULL) {
1697a04a10f8SKris Kennaway 		restore_uid();
1698a04a10f8SKris Kennaway 		error("private X11 dir: mkdtemp %s failed: %s",
1699a04a10f8SKris Kennaway 		    xauthfile, strerror(errno));
1700a04a10f8SKris Kennaway 		xfree(xauthfile);
1701a04a10f8SKris Kennaway 		xauthfile = NULL;
1702a04a10f8SKris Kennaway 		xfree(s->auth_proto);
1703a04a10f8SKris Kennaway 		xfree(s->auth_data);
1704a04a10f8SKris Kennaway 		/* XXXX remove listening channels */
1705a04a10f8SKris Kennaway 		return 0;
1706a04a10f8SKris Kennaway 	}
1707a04a10f8SKris Kennaway 	strlcat(xauthfile, "/cookies", MAXPATHLEN);
1708c2d3a559SKris Kennaway 	fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
1709c2d3a559SKris Kennaway 	if (fd >= 0)
1710c2d3a559SKris Kennaway 		close(fd);
1711a04a10f8SKris Kennaway 	restore_uid();
1712e9fd63dfSBrian Feldman 	fatal_add_cleanup(xauthfile_cleanup_proc, s->pw);
1713a04a10f8SKris Kennaway 	return 1;
1714a04a10f8SKris Kennaway }
1715a04a10f8SKris Kennaway 
1716c2d3a559SKris Kennaway int
1717c2d3a559SKris Kennaway session_shell_req(Session *s)
1718c2d3a559SKris Kennaway {
1719c2d3a559SKris Kennaway 	/* if forced_command == NULL, the shell is execed */
1720c2d3a559SKris Kennaway 	char *shell = forced_command;
1721c2d3a559SKris Kennaway 	packet_done();
1722c2d3a559SKris Kennaway 	if (s->ttyfd == -1)
1723ca3176e7SBrian Feldman 		do_exec_no_pty(s, shell);
1724c2d3a559SKris Kennaway 	else
1725ca3176e7SBrian Feldman 		do_exec_pty(s, shell);
1726c2d3a559SKris Kennaway 	return 1;
1727c2d3a559SKris Kennaway }
1728c2d3a559SKris Kennaway 
1729c2d3a559SKris Kennaway int
1730c2d3a559SKris Kennaway session_exec_req(Session *s)
1731c2d3a559SKris Kennaway {
1732ca3176e7SBrian Feldman 	u_int len;
1733c2d3a559SKris Kennaway 	char *command = packet_get_string(&len);
1734c2d3a559SKris Kennaway 	packet_done();
1735c2d3a559SKris Kennaway 	if (forced_command) {
1736c2d3a559SKris Kennaway 		original_command = command;
1737c2d3a559SKris Kennaway 		command = forced_command;
1738c2d3a559SKris Kennaway 		debug("Forced command '%.500s'", forced_command);
1739c2d3a559SKris Kennaway 	}
1740c2d3a559SKris Kennaway 	if (s->ttyfd == -1)
1741ca3176e7SBrian Feldman 		do_exec_no_pty(s, command);
1742c2d3a559SKris Kennaway 	else
1743ca3176e7SBrian Feldman 		do_exec_pty(s, command);
1744c2d3a559SKris Kennaway 	if (forced_command == NULL)
1745c2d3a559SKris Kennaway 		xfree(command);
1746c2d3a559SKris Kennaway 	return 1;
1747c2d3a559SKris Kennaway }
1748c2d3a559SKris Kennaway 
1749ca3176e7SBrian Feldman int
1750ca3176e7SBrian Feldman session_auth_agent_req(Session *s)
1751ca3176e7SBrian Feldman {
1752ca3176e7SBrian Feldman 	static int called = 0;
1753ca3176e7SBrian Feldman 	packet_done();
1754ca3176e7SBrian Feldman 	if (no_agent_forwarding_flag) {
1755ca3176e7SBrian Feldman 		debug("session_auth_agent_req: no_agent_forwarding_flag");
1756ca3176e7SBrian Feldman 		return 0;
1757ca3176e7SBrian Feldman 	}
1758ca3176e7SBrian Feldman 	if (called) {
1759ca3176e7SBrian Feldman 		return 0;
1760ca3176e7SBrian Feldman 	} else {
1761ca3176e7SBrian Feldman 		called = 1;
1762ca3176e7SBrian Feldman 		return auth_input_request_forwarding(s->pw);
1763ca3176e7SBrian Feldman 	}
1764ca3176e7SBrian Feldman }
1765ca3176e7SBrian Feldman 
1766a04a10f8SKris Kennaway void
1767a04a10f8SKris Kennaway session_input_channel_req(int id, void *arg)
1768a04a10f8SKris Kennaway {
1769ca3176e7SBrian Feldman 	u_int len;
1770a04a10f8SKris Kennaway 	int reply;
1771a04a10f8SKris Kennaway 	int success = 0;
1772a04a10f8SKris Kennaway 	char *rtype;
1773a04a10f8SKris Kennaway 	Session *s;
1774a04a10f8SKris Kennaway 	Channel *c;
1775a04a10f8SKris Kennaway 
1776a04a10f8SKris Kennaway 	rtype = packet_get_string(&len);
1777a04a10f8SKris Kennaway 	reply = packet_get_char();
1778a04a10f8SKris Kennaway 
1779a04a10f8SKris Kennaway 	s = session_by_channel(id);
1780a04a10f8SKris Kennaway 	if (s == NULL)
1781a04a10f8SKris Kennaway 		fatal("session_input_channel_req: channel %d: no session", id);
1782a04a10f8SKris Kennaway 	c = channel_lookup(id);
1783a04a10f8SKris Kennaway 	if (c == NULL)
1784a04a10f8SKris Kennaway 		fatal("session_input_channel_req: channel %d: bad channel", id);
1785a04a10f8SKris Kennaway 
1786a04a10f8SKris Kennaway 	debug("session_input_channel_req: session %d channel %d request %s reply %d",
1787a04a10f8SKris Kennaway 	    s->self, id, rtype, reply);
1788a04a10f8SKris Kennaway 
1789a04a10f8SKris Kennaway 	/*
1790ca3176e7SBrian Feldman 	 * a session is in LARVAL state until a shell, a command
1791ca3176e7SBrian Feldman 	 * or a subsystem is executed
1792a04a10f8SKris Kennaway 	 */
1793a04a10f8SKris Kennaway 	if (c->type == SSH_CHANNEL_LARVAL) {
1794a04a10f8SKris Kennaway 		if (strcmp(rtype, "shell") == 0) {
1795c2d3a559SKris Kennaway 			success = session_shell_req(s);
1796a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "exec") == 0) {
1797c2d3a559SKris Kennaway 			success = session_exec_req(s);
1798a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "pty-req") == 0) {
1799a04a10f8SKris Kennaway 			success =  session_pty_req(s);
1800a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "x11-req") == 0) {
1801a04a10f8SKris Kennaway 			success = session_x11_req(s);
1802ca3176e7SBrian Feldman 		} else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
1803ca3176e7SBrian Feldman 			success = session_auth_agent_req(s);
1804a04a10f8SKris Kennaway 		} else if (strcmp(rtype, "subsystem") == 0) {
1805a04a10f8SKris Kennaway 			success = session_subsystem_req(s);
1806a04a10f8SKris Kennaway 		}
1807a04a10f8SKris Kennaway 	}
1808a04a10f8SKris Kennaway 	if (strcmp(rtype, "window-change") == 0) {
1809a04a10f8SKris Kennaway 		success = session_window_change_req(s);
1810a04a10f8SKris Kennaway 	}
1811a04a10f8SKris Kennaway 
1812a04a10f8SKris Kennaway 	if (reply) {
1813a04a10f8SKris Kennaway 		packet_start(success ?
1814a04a10f8SKris Kennaway 		    SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
1815a04a10f8SKris Kennaway 		packet_put_int(c->remote_id);
1816a04a10f8SKris Kennaway 		packet_send();
1817a04a10f8SKris Kennaway 	}
1818a04a10f8SKris Kennaway 	xfree(rtype);
1819a04a10f8SKris Kennaway }
1820a04a10f8SKris Kennaway 
1821a04a10f8SKris Kennaway void
1822a04a10f8SKris Kennaway session_set_fds(Session *s, int fdin, int fdout, int fderr)
1823a04a10f8SKris Kennaway {
1824a04a10f8SKris Kennaway 	if (!compat20)
1825a04a10f8SKris Kennaway 		fatal("session_set_fds: called for proto != 2.0");
1826a04a10f8SKris Kennaway 	/*
1827a04a10f8SKris Kennaway 	 * now that have a child and a pipe to the child,
1828a04a10f8SKris Kennaway 	 * we can activate our channel and register the fd's
1829a04a10f8SKris Kennaway 	 */
1830a04a10f8SKris Kennaway 	if (s->chanid == -1)
1831a04a10f8SKris Kennaway 		fatal("no channel for session %d", s->self);
1832a04a10f8SKris Kennaway 	channel_set_fds(s->chanid,
1833a04a10f8SKris Kennaway 	    fdout, fdin, fderr,
183409958426SBrian Feldman 	    fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
183509958426SBrian Feldman 	    1);
1836a04a10f8SKris Kennaway }
1837a04a10f8SKris Kennaway 
1838a04a10f8SKris Kennaway void
1839a04a10f8SKris Kennaway session_pty_cleanup(Session *s)
1840a04a10f8SKris Kennaway {
1841a04a10f8SKris Kennaway 	if (s == NULL || s->ttyfd == -1)
1842a04a10f8SKris Kennaway 		return;
1843a04a10f8SKris Kennaway 
1844ca3176e7SBrian Feldman 	debug("session_pty_cleanup: session %d release %s", s->self, s->tty);
1845a04a10f8SKris Kennaway 
1846a04a10f8SKris Kennaway 	/* Cancel the cleanup function. */
1847a04a10f8SKris Kennaway 	fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
1848a04a10f8SKris Kennaway 
1849a04a10f8SKris Kennaway 	/* Record that the user has logged out. */
1850a04a10f8SKris Kennaway 	record_logout(s->pid, s->tty);
1851a04a10f8SKris Kennaway 
1852a04a10f8SKris Kennaway 	/* Release the pseudo-tty. */
1853a04a10f8SKris Kennaway 	pty_release(s->tty);
1854a04a10f8SKris Kennaway 
1855a04a10f8SKris Kennaway 	/*
1856a04a10f8SKris Kennaway 	 * Close the server side of the socket pairs.  We must do this after
1857a04a10f8SKris Kennaway 	 * the pty cleanup, so that another process doesn't get this pty
1858a04a10f8SKris Kennaway 	 * while we're still cleaning up.
1859a04a10f8SKris Kennaway 	 */
1860a04a10f8SKris Kennaway 	if (close(s->ptymaster) < 0)
1861a04a10f8SKris Kennaway 		error("close(s->ptymaster): %s", strerror(errno));
1862a04a10f8SKris Kennaway }
1863a04a10f8SKris Kennaway 
1864a04a10f8SKris Kennaway void
1865a04a10f8SKris Kennaway session_exit_message(Session *s, int status)
1866a04a10f8SKris Kennaway {
1867a04a10f8SKris Kennaway 	Channel *c;
1868a04a10f8SKris Kennaway 	if (s == NULL)
1869a04a10f8SKris Kennaway 		fatal("session_close: no session");
1870a04a10f8SKris Kennaway 	c = channel_lookup(s->chanid);
1871a04a10f8SKris Kennaway 	if (c == NULL)
1872a04a10f8SKris Kennaway 		fatal("session_close: session %d: no channel %d",
1873a04a10f8SKris Kennaway 		    s->self, s->chanid);
1874a04a10f8SKris Kennaway 	debug("session_exit_message: session %d channel %d pid %d",
1875a04a10f8SKris Kennaway 	    s->self, s->chanid, s->pid);
1876a04a10f8SKris Kennaway 
1877a04a10f8SKris Kennaway 	if (WIFEXITED(status)) {
1878a04a10f8SKris Kennaway 		channel_request_start(s->chanid,
1879a04a10f8SKris Kennaway 		    "exit-status", 0);
1880a04a10f8SKris Kennaway 		packet_put_int(WEXITSTATUS(status));
1881a04a10f8SKris Kennaway 		packet_send();
1882a04a10f8SKris Kennaway 	} else if (WIFSIGNALED(status)) {
1883a04a10f8SKris Kennaway 		channel_request_start(s->chanid,
1884a04a10f8SKris Kennaway 		    "exit-signal", 0);
1885a04a10f8SKris Kennaway 		packet_put_int(WTERMSIG(status));
1886a04a10f8SKris Kennaway 		packet_put_char(WCOREDUMP(status));
1887a04a10f8SKris Kennaway 		packet_put_cstring("");
1888a04a10f8SKris Kennaway 		packet_put_cstring("");
1889a04a10f8SKris Kennaway 		packet_send();
1890a04a10f8SKris Kennaway 	} else {
1891a04a10f8SKris Kennaway 		/* Some weird exit cause.  Just exit. */
1892a04a10f8SKris Kennaway 		packet_disconnect("wait returned status %04x.", status);
1893a04a10f8SKris Kennaway 	}
1894a04a10f8SKris Kennaway 
1895a04a10f8SKris Kennaway 	/* disconnect channel */
1896a04a10f8SKris Kennaway 	debug("session_exit_message: release channel %d", s->chanid);
1897a04a10f8SKris Kennaway 	channel_cancel_cleanup(s->chanid);
1898a04a10f8SKris Kennaway 	/*
1899a04a10f8SKris Kennaway 	 * emulate a write failure with 'chan_write_failed', nobody will be
1900a04a10f8SKris Kennaway 	 * interested in data we write.
1901a04a10f8SKris Kennaway 	 * Note that we must not call 'chan_read_failed', since there could
1902a04a10f8SKris Kennaway 	 * be some more data waiting in the pipe.
1903a04a10f8SKris Kennaway 	 */
1904a04a10f8SKris Kennaway 	if (c->ostate != CHAN_OUTPUT_CLOSED)
1905a04a10f8SKris Kennaway 		chan_write_failed(c);
1906a04a10f8SKris Kennaway 	s->chanid = -1;
1907a04a10f8SKris Kennaway }
1908a04a10f8SKris Kennaway 
1909a04a10f8SKris Kennaway void
1910a04a10f8SKris Kennaway session_free(Session *s)
1911a04a10f8SKris Kennaway {
1912a04a10f8SKris Kennaway 	debug("session_free: session %d pid %d", s->self, s->pid);
1913a04a10f8SKris Kennaway 	if (s->term)
1914a04a10f8SKris Kennaway 		xfree(s->term);
1915a04a10f8SKris Kennaway 	if (s->display)
1916a04a10f8SKris Kennaway 		xfree(s->display);
1917a04a10f8SKris Kennaway 	if (s->auth_data)
1918a04a10f8SKris Kennaway 		xfree(s->auth_data);
1919a04a10f8SKris Kennaway 	if (s->auth_proto)
1920a04a10f8SKris Kennaway 		xfree(s->auth_proto);
1921a04a10f8SKris Kennaway 	s->used = 0;
1922a04a10f8SKris Kennaway }
1923a04a10f8SKris Kennaway 
1924a04a10f8SKris Kennaway void
1925a04a10f8SKris Kennaway session_close(Session *s)
1926a04a10f8SKris Kennaway {
1927a04a10f8SKris Kennaway 	session_pty_cleanup(s);
1928a04a10f8SKris Kennaway 	session_free(s);
1929a04a10f8SKris Kennaway 	session_proctitle(s);
1930a04a10f8SKris Kennaway }
1931a04a10f8SKris Kennaway 
1932a04a10f8SKris Kennaway void
1933a04a10f8SKris Kennaway session_close_by_pid(pid_t pid, int status)
1934a04a10f8SKris Kennaway {
1935a04a10f8SKris Kennaway 	Session *s = session_by_pid(pid);
1936a04a10f8SKris Kennaway 	if (s == NULL) {
1937a04a10f8SKris Kennaway 		debug("session_close_by_pid: no session for pid %d", s->pid);
1938a04a10f8SKris Kennaway 		return;
1939a04a10f8SKris Kennaway 	}
1940a04a10f8SKris Kennaway 	if (s->chanid != -1)
1941a04a10f8SKris Kennaway 		session_exit_message(s, status);
1942a04a10f8SKris Kennaway 	session_close(s);
1943a04a10f8SKris Kennaway }
1944a04a10f8SKris Kennaway 
1945a04a10f8SKris Kennaway /*
1946a04a10f8SKris Kennaway  * this is called when a channel dies before
1947a04a10f8SKris Kennaway  * the session 'child' itself dies
1948a04a10f8SKris Kennaway  */
1949a04a10f8SKris Kennaway void
1950a04a10f8SKris Kennaway session_close_by_channel(int id, void *arg)
1951a04a10f8SKris Kennaway {
1952a04a10f8SKris Kennaway 	Session *s = session_by_channel(id);
1953a04a10f8SKris Kennaway 	if (s == NULL) {
1954a04a10f8SKris Kennaway 		debug("session_close_by_channel: no session for channel %d", id);
1955a04a10f8SKris Kennaway 		return;
1956a04a10f8SKris Kennaway 	}
1957a04a10f8SKris Kennaway 	/* disconnect channel */
1958a04a10f8SKris Kennaway 	channel_cancel_cleanup(s->chanid);
1959a04a10f8SKris Kennaway 	s->chanid = -1;
1960a04a10f8SKris Kennaway 
1961a04a10f8SKris Kennaway 	debug("session_close_by_channel: channel %d kill %d", id, s->pid);
1962a04a10f8SKris Kennaway 	if (s->pid == 0) {
1963a04a10f8SKris Kennaway 		/* close session immediately */
1964a04a10f8SKris Kennaway 		session_close(s);
1965a04a10f8SKris Kennaway 	} else {
1966a04a10f8SKris Kennaway 		/* notify child, delay session cleanup */
196709958426SBrian Feldman 		if (s->pid <= 1)
196809958426SBrian Feldman 			fatal("session_close_by_channel: Unsafe s->pid = %d", s->pid);
1969a04a10f8SKris Kennaway 		if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
1970a04a10f8SKris Kennaway 			error("session_close_by_channel: kill %d: %s",
1971a04a10f8SKris Kennaway 			    s->pid, strerror(errno));
1972a04a10f8SKris Kennaway 	}
1973a04a10f8SKris Kennaway }
1974a04a10f8SKris Kennaway 
1975a04a10f8SKris Kennaway char *
1976a04a10f8SKris Kennaway session_tty_list(void)
1977a04a10f8SKris Kennaway {
1978a04a10f8SKris Kennaway 	static char buf[1024];
1979a04a10f8SKris Kennaway 	int i;
1980a04a10f8SKris Kennaway 	buf[0] = '\0';
1981a04a10f8SKris Kennaway 	for(i = 0; i < MAX_SESSIONS; i++) {
1982a04a10f8SKris Kennaway 		Session *s = &sessions[i];
1983a04a10f8SKris Kennaway 		if (s->used && s->ttyfd != -1) {
1984a04a10f8SKris Kennaway 			if (buf[0] != '\0')
1985a04a10f8SKris Kennaway 				strlcat(buf, ",", sizeof buf);
1986a04a10f8SKris Kennaway 			strlcat(buf, strrchr(s->tty, '/') + 1, sizeof buf);
1987a04a10f8SKris Kennaway 		}
1988a04a10f8SKris Kennaway 	}
1989a04a10f8SKris Kennaway 	if (buf[0] == '\0')
1990a04a10f8SKris Kennaway 		strlcpy(buf, "notty", sizeof buf);
1991a04a10f8SKris Kennaway 	return buf;
1992a04a10f8SKris Kennaway }
1993a04a10f8SKris Kennaway 
1994a04a10f8SKris Kennaway void
1995a04a10f8SKris Kennaway session_proctitle(Session *s)
1996a04a10f8SKris Kennaway {
1997a04a10f8SKris Kennaway 	if (s->pw == NULL)
1998a04a10f8SKris Kennaway 		error("no user for session %d", s->self);
1999a04a10f8SKris Kennaway 	else
2000a04a10f8SKris Kennaway 		setproctitle("%s@%s", s->pw->pw_name, session_tty_list());
2001a04a10f8SKris Kennaway }
2002a04a10f8SKris Kennaway 
2003a04a10f8SKris Kennaway void
2004ca3176e7SBrian Feldman do_authenticated2(Authctxt *authctxt)
2005a04a10f8SKris Kennaway {
2006c2d3a559SKris Kennaway 
2007a04a10f8SKris Kennaway 	server_loop2();
2008a04a10f8SKris Kennaway }
2009