blob: c6253139c9e7400455e0562e975c9e42792a87a2 [file] [log] [blame]
Damien Millerb38eff82000-04-01 11:09:21 +10001/*
2 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
3 * All rights reserved
4 */
Damien Millerefb4afe2000-04-12 18:45:05 +10005/*
6 * SSH2 support by Markus Friedl.
7 * Copyright (c) 2000 Markus Friedl. All rights reserved.
8 */
Damien Millerb38eff82000-04-01 11:09:21 +10009
10#include "includes.h"
Damien Miller1b26ab22000-04-30 10:12:49 +100011RCSID("$OpenBSD: session.c,v 1.8 2000/04/29 16:06:08 markus Exp $");
Damien Millerb38eff82000-04-01 11:09:21 +100012
13#include "xmalloc.h"
14#include "ssh.h"
15#include "pty.h"
16#include "packet.h"
17#include "buffer.h"
18#include "cipher.h"
19#include "mpaux.h"
20#include "servconf.h"
21#include "uidswap.h"
22#include "compat.h"
23#include "channels.h"
24#include "nchan.h"
25
Damien Millerefb4afe2000-04-12 18:45:05 +100026#include "bufaux.h"
27#include "ssh2.h"
28#include "auth.h"
29
Damien Millerb38eff82000-04-01 11:09:21 +100030/* types */
31
32#define TTYSZ 64
33typedef struct Session Session;
34struct Session {
35 int used;
36 int self;
Damien Millerbd483e72000-04-30 10:00:53 +100037 int extended;
Damien Millerb38eff82000-04-01 11:09:21 +100038 struct passwd *pw;
39 pid_t pid;
40 /* tty */
41 char *term;
42 int ptyfd, ttyfd, ptymaster;
43 int row, col, xpixel, ypixel;
44 char tty[TTYSZ];
45 /* X11 */
46 char *display;
47 int screen;
48 char *auth_proto;
49 char *auth_data;
Damien Millerbd483e72000-04-30 10:00:53 +100050 int single_connection;
Damien Millerb38eff82000-04-01 11:09:21 +100051 /* proto 2 */
52 int chanid;
53};
54
55/* func */
56
57Session *session_new(void);
58void session_set_fds(Session *s, int fdin, int fdout, int fderr);
59void session_pty_cleanup(Session *s);
60void do_exec_pty(Session *s, const char *command, struct passwd * pw);
61void do_exec_no_pty(Session *s, const char *command, struct passwd * pw);
62
63void
64do_child(const char *command, struct passwd * pw, const char *term,
65 const char *display, const char *auth_proto,
66 const char *auth_data, const char *ttyname);
67
68/* import */
69extern ServerOptions options;
Damien Miller06d84b72000-04-21 16:13:07 +100070#ifdef HAVE___PROGNAME
Damien Millerb38eff82000-04-01 11:09:21 +100071extern char *__progname;
Damien Miller06d84b72000-04-21 16:13:07 +100072#else /* HAVE___PROGNAME */
73const char *__progname = "sshd";
74#endif /* HAVE___PROGNAME */
75
Damien Millerb38eff82000-04-01 11:09:21 +100076extern int log_stderr;
77extern int debug_flag;
78
79/* Local Xauthority file. */
80static char *xauthfile;
81
82/* data */
83#define MAX_SESSIONS 10
84Session sessions[MAX_SESSIONS];
85
86/* Flags set in auth-rsa from authorized_keys flags. These are set in auth-rsa.c. */
87int no_port_forwarding_flag = 0;
88int no_agent_forwarding_flag = 0;
89int no_x11_forwarding_flag = 0;
90int no_pty_flag = 0;
91
92/* RSA authentication "command=" option. */
93char *forced_command = NULL;
94
95/* RSA authentication "environment=" options. */
96struct envstring *custom_environment = NULL;
97
98/*
99 * Remove local Xauthority file.
100 */
101void
102xauthfile_cleanup_proc(void *ignore)
103{
104 debug("xauthfile_cleanup_proc called");
105
106 if (xauthfile != NULL) {
107 char *p;
108 unlink(xauthfile);
109 p = strrchr(xauthfile, '/');
110 if (p != NULL) {
111 *p = '\0';
112 rmdir(xauthfile);
113 }
114 xfree(xauthfile);
115 xauthfile = NULL;
116 }
117}
118
119/*
120 * Function to perform cleanup if we get aborted abnormally (e.g., due to a
121 * dropped connection).
122 */
Damien Miller4af51302000-04-16 11:18:38 +1000123void
Damien Millerb38eff82000-04-01 11:09:21 +1000124pty_cleanup_proc(void *session)
125{
126 Session *s=session;
127 if (s == NULL)
128 fatal("pty_cleanup_proc: no session");
129 debug("pty_cleanup_proc: %s", s->tty);
130
131 if (s->pid != 0) {
132 /* Record that the user has logged out. */
133 record_logout(s->pid, s->tty);
134 }
135
136 /* Release the pseudo-tty. */
137 pty_release(s->tty);
138}
139
140/*
141 * Prepares for an interactive session. This is called after the user has
142 * been successfully authenticated. During this message exchange, pseudo
143 * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
144 * are requested, etc.
145 */
Damien Miller4af51302000-04-16 11:18:38 +1000146void
Damien Millerb38eff82000-04-01 11:09:21 +1000147do_authenticated(struct passwd * pw)
148{
149 Session *s;
150 int type;
151 int compression_level = 0, enable_compression_after_reply = 0;
152 int have_pty = 0;
153 char *command;
154 int n_bytes;
155 int plen;
156 unsigned int proto_len, data_len, dlen;
157
158 /*
159 * Cancel the alarm we set to limit the time taken for
160 * authentication.
161 */
162 alarm(0);
163
164 /*
165 * Inform the channel mechanism that we are the server side and that
166 * the client may request to connect to any port at all. (The user
167 * could do it anyway, and we wouldn\'t know what is permitted except
168 * by the client telling us, so we can equally well trust the client
169 * not to request anything bogus.)
170 */
171 if (!no_port_forwarding_flag)
172 channel_permit_all_opens();
173
174 s = session_new();
Damien Millerbd483e72000-04-30 10:00:53 +1000175 s->pw = pw;
Damien Millerb38eff82000-04-01 11:09:21 +1000176
177 /*
178 * We stay in this loop until the client requests to execute a shell
179 * or a command.
180 */
181 for (;;) {
182 int success = 0;
183
184 /* Get a packet from the client. */
185 type = packet_read(&plen);
186
187 /* Process the packet. */
188 switch (type) {
189 case SSH_CMSG_REQUEST_COMPRESSION:
190 packet_integrity_check(plen, 4, type);
191 compression_level = packet_get_int();
192 if (compression_level < 1 || compression_level > 9) {
193 packet_send_debug("Received illegal compression level %d.",
194 compression_level);
195 break;
196 }
197 /* Enable compression after we have responded with SUCCESS. */
198 enable_compression_after_reply = 1;
199 success = 1;
200 break;
201
202 case SSH_CMSG_REQUEST_PTY:
203 if (no_pty_flag) {
204 debug("Allocating a pty not permitted for this authentication.");
205 break;
206 }
207 if (have_pty)
208 packet_disconnect("Protocol error: you already have a pty.");
209
210 debug("Allocating pty.");
211
212 /* Allocate a pty and open it. */
213 if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
214 sizeof(s->tty))) {
215 error("Failed to allocate pty.");
216 break;
217 }
218 fatal_add_cleanup(pty_cleanup_proc, (void *)s);
219 pty_setowner(pw, s->tty);
220
221 /* Get TERM from the packet. Note that the value may be of arbitrary length. */
222 s->term = packet_get_string(&dlen);
223 packet_integrity_check(dlen, strlen(s->term), type);
224 /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
225 /* Remaining bytes */
226 n_bytes = plen - (4 + dlen + 4 * 4);
227
228 if (strcmp(s->term, "") == 0) {
229 xfree(s->term);
230 s->term = NULL;
231 }
232 /* Get window size from the packet. */
233 s->row = packet_get_int();
234 s->col = packet_get_int();
235 s->xpixel = packet_get_int();
236 s->ypixel = packet_get_int();
237 pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
238
239 /* Get tty modes from the packet. */
240 tty_parse_modes(s->ttyfd, &n_bytes);
241 packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
242
243 /* Indicate that we now have a pty. */
244 success = 1;
245 have_pty = 1;
246 break;
247
248 case SSH_CMSG_X11_REQUEST_FORWARDING:
249 if (!options.x11_forwarding) {
250 packet_send_debug("X11 forwarding disabled in server configuration file.");
251 break;
252 }
253#ifdef XAUTH_PATH
254 if (no_x11_forwarding_flag) {
255 packet_send_debug("X11 forwarding not permitted for this authentication.");
256 break;
257 }
258 debug("Received request for X11 forwarding with auth spoofing.");
259 if (s->display != NULL)
260 packet_disconnect("Protocol error: X11 display already set.");
261
262 s->auth_proto = packet_get_string(&proto_len);
263 s->auth_data = packet_get_string(&data_len);
264 packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
265
266 if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
267 s->screen = packet_get_int();
268 else
269 s->screen = 0;
270 s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
271
272 if (s->display == NULL)
273 break;
274
275 /* Setup to always have a local .Xauthority. */
276 xauthfile = xmalloc(MAXPATHLEN);
277 strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
278 temporarily_use_uid(pw->pw_uid);
279 if (mkdtemp(xauthfile) == NULL) {
280 restore_uid();
281 error("private X11 dir: mkdtemp %s failed: %s",
282 xauthfile, strerror(errno));
283 xfree(xauthfile);
284 xauthfile = NULL;
Damien Millerbd483e72000-04-30 10:00:53 +1000285 /* XXXX remove listening channels */
Damien Millerb38eff82000-04-01 11:09:21 +1000286 break;
287 }
288 strlcat(xauthfile, "/cookies", MAXPATHLEN);
289 open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
290 restore_uid();
291 fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
292 success = 1;
293 break;
294#else /* XAUTH_PATH */
295 packet_send_debug("No xauth program; cannot forward with spoofing.");
296 break;
297#endif /* XAUTH_PATH */
298
299 case SSH_CMSG_AGENT_REQUEST_FORWARDING:
300 if (no_agent_forwarding_flag || compat13) {
301 debug("Authentication agent forwarding not permitted for this authentication.");
302 break;
303 }
304 debug("Received authentication agent forwarding request.");
305 auth_input_request_forwarding(pw);
306 success = 1;
307 break;
308
309 case SSH_CMSG_PORT_FORWARD_REQUEST:
310 if (no_port_forwarding_flag) {
311 debug("Port forwarding not permitted for this authentication.");
312 break;
313 }
314 debug("Received TCP/IP port forwarding request.");
315 channel_input_port_forward_request(pw->pw_uid == 0);
316 success = 1;
317 break;
318
319 case SSH_CMSG_MAX_PACKET_SIZE:
320 if (packet_set_maxsize(packet_get_int()) > 0)
321 success = 1;
322 break;
323
324 case SSH_CMSG_EXEC_SHELL:
325 case SSH_CMSG_EXEC_CMD:
326 /* Set interactive/non-interactive mode. */
327 packet_set_interactive(have_pty || s->display != NULL,
328 options.keepalives);
329
330 if (type == SSH_CMSG_EXEC_CMD) {
331 command = packet_get_string(&dlen);
332 debug("Exec command '%.500s'", command);
333 packet_integrity_check(plen, 4 + dlen, type);
334 } else {
335 command = NULL;
336 packet_integrity_check(plen, 0, type);
337 }
338 if (forced_command != NULL) {
339 command = forced_command;
340 debug("Forced command '%.500s'", forced_command);
341 }
342 if (have_pty)
343 do_exec_pty(s, command, pw);
344 else
345 do_exec_no_pty(s, command, pw);
346
347 if (command != NULL)
348 xfree(command);
349 /* Cleanup user's local Xauthority file. */
350 if (xauthfile)
351 xauthfile_cleanup_proc(NULL);
352 return;
353
354 default:
355 /*
356 * Any unknown messages in this phase are ignored,
357 * and a failure message is returned.
358 */
359 log("Unknown packet type received after authentication: %d", type);
360 }
361 packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
362 packet_send();
363 packet_write_wait();
364
365 /* Enable compression now that we have replied if appropriate. */
366 if (enable_compression_after_reply) {
367 enable_compression_after_reply = 0;
368 packet_start_compression(compression_level);
369 }
370 }
371}
372
373/*
374 * This is called to fork and execute a command when we have no tty. This
375 * will call do_child from the child, and server_loop from the parent after
376 * setting up file descriptors and such.
377 */
Damien Miller4af51302000-04-16 11:18:38 +1000378void
Damien Millerb38eff82000-04-01 11:09:21 +1000379do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
380{
381 int pid;
382
383#ifdef USE_PIPES
384 int pin[2], pout[2], perr[2];
385 /* Allocate pipes for communicating with the program. */
386 if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
387 packet_disconnect("Could not create pipes: %.100s",
388 strerror(errno));
389#else /* USE_PIPES */
390 int inout[2], err[2];
391 /* Uses socket pairs to communicate with the program. */
392 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
393 socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
394 packet_disconnect("Could not create socket pairs: %.100s",
395 strerror(errno));
396#endif /* USE_PIPES */
397 if (s == NULL)
398 fatal("do_exec_no_pty: no session");
399
400 setproctitle("%s@notty", pw->pw_name);
401
402#ifdef USE_PAM
403 do_pam_setcred();
404#endif /* USE_PAM */
405
406 /* Fork the child. */
407 if ((pid = fork()) == 0) {
408 /* Child. Reinitialize the log since the pid has changed. */
409 log_init(__progname, options.log_level, options.log_facility, log_stderr);
410
411 /*
412 * Create a new session and process group since the 4.4BSD
413 * setlogin() affects the entire process group.
414 */
415 if (setsid() < 0)
416 error("setsid failed: %.100s", strerror(errno));
417
418#ifdef USE_PIPES
419 /*
420 * Redirect stdin. We close the parent side of the socket
421 * pair, and make the child side the standard input.
422 */
423 close(pin[1]);
424 if (dup2(pin[0], 0) < 0)
425 perror("dup2 stdin");
426 close(pin[0]);
427
428 /* Redirect stdout. */
429 close(pout[0]);
430 if (dup2(pout[1], 1) < 0)
431 perror("dup2 stdout");
432 close(pout[1]);
433
434 /* Redirect stderr. */
435 close(perr[0]);
436 if (dup2(perr[1], 2) < 0)
437 perror("dup2 stderr");
438 close(perr[1]);
439#else /* USE_PIPES */
440 /*
441 * Redirect stdin, stdout, and stderr. Stdin and stdout will
442 * use the same socket, as some programs (particularly rdist)
443 * seem to depend on it.
444 */
445 close(inout[1]);
446 close(err[1]);
447 if (dup2(inout[0], 0) < 0) /* stdin */
448 perror("dup2 stdin");
449 if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */
450 perror("dup2 stdout");
451 if (dup2(err[0], 2) < 0) /* stderr */
452 perror("dup2 stderr");
453#endif /* USE_PIPES */
454
455 /* Do processing for the child (exec command etc). */
456 do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL);
457 /* NOTREACHED */
458 }
459 if (pid < 0)
460 packet_disconnect("fork failed: %.100s", strerror(errno));
461 s->pid = pid;
462#ifdef USE_PIPES
463 /* We are the parent. Close the child sides of the pipes. */
464 close(pin[0]);
465 close(pout[1]);
466 close(perr[1]);
467
Damien Millerefb4afe2000-04-12 18:45:05 +1000468 if (compat20) {
Damien Millerbd483e72000-04-30 10:00:53 +1000469 session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1);
Damien Millerefb4afe2000-04-12 18:45:05 +1000470 } else {
471 /* Enter the interactive session. */
472 server_loop(pid, pin[1], pout[0], perr[0]);
473 /* server_loop has closed pin[1], pout[1], and perr[1]. */
474 }
Damien Millerb38eff82000-04-01 11:09:21 +1000475#else /* USE_PIPES */
476 /* We are the parent. Close the child sides of the socket pairs. */
477 close(inout[0]);
478 close(err[0]);
479
480 /*
481 * Enter the interactive session. Note: server_loop must be able to
482 * handle the case that fdin and fdout are the same.
483 */
Damien Millerefb4afe2000-04-12 18:45:05 +1000484 if (compat20) {
Damien Millerbd483e72000-04-30 10:00:53 +1000485 session_set_fds(s, inout[1], inout[1], s->extended ? err[1] : -1);
Damien Millerefb4afe2000-04-12 18:45:05 +1000486 } else {
487 server_loop(pid, inout[1], inout[1], err[1]);
488 /* server_loop has closed inout[1] and err[1]. */
489 }
Damien Millerb38eff82000-04-01 11:09:21 +1000490#endif /* USE_PIPES */
491}
492
493/*
494 * This is called to fork and execute a command when we have a tty. This
495 * will call do_child from the child, and server_loop from the parent after
496 * setting up file descriptors, controlling tty, updating wtmp, utmp,
497 * lastlog, and other such operations.
498 */
Damien Miller4af51302000-04-16 11:18:38 +1000499void
Damien Millerb38eff82000-04-01 11:09:21 +1000500do_exec_pty(Session *s, const char *command, struct passwd * pw)
501{
502 FILE *f;
503 char buf[100], *time_string;
504 char line[256];
505 const char *hostname;
506 int fdout, ptyfd, ttyfd, ptymaster;
507 int quiet_login;
508 pid_t pid;
509 socklen_t fromlen;
510 struct sockaddr_storage from;
511 struct stat st;
512 time_t last_login_time;
513
514 if (s == NULL)
515 fatal("do_exec_pty: no session");
516 ptyfd = s->ptyfd;
517 ttyfd = s->ttyfd;
518
519 /* Get remote host name. */
520 hostname = get_canonical_hostname();
521
522 /*
523 * Get the time when the user last logged in. Buf will be set to
524 * contain the hostname the last login was from.
525 */
526 if (!options.use_login) {
527 last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
528 buf, sizeof(buf));
529 }
530 setproctitle("%s@%s", pw->pw_name, strrchr(s->tty, '/') + 1);
531
532#ifdef USE_PAM
533 do_pam_session(pw->pw_name, s->tty);
534 do_pam_setcred();
535#endif /* USE_PAM */
536
537 /* Fork the child. */
538 if ((pid = fork()) == 0) {
539 pid = getpid();
540
541 /* Child. Reinitialize the log because the pid has
542 changed. */
543 log_init(__progname, options.log_level, options.log_facility, log_stderr);
544
545 /* Close the master side of the pseudo tty. */
546 close(ptyfd);
547
548 /* Make the pseudo tty our controlling tty. */
549 pty_make_controlling_tty(&ttyfd, s->tty);
550
551 /* Redirect stdin from the pseudo tty. */
552 if (dup2(ttyfd, fileno(stdin)) < 0)
553 error("dup2 stdin failed: %.100s", strerror(errno));
554
555 /* Redirect stdout to the pseudo tty. */
556 if (dup2(ttyfd, fileno(stdout)) < 0)
557 error("dup2 stdin failed: %.100s", strerror(errno));
558
559 /* Redirect stderr to the pseudo tty. */
560 if (dup2(ttyfd, fileno(stderr)) < 0)
561 error("dup2 stdin failed: %.100s", strerror(errno));
562
563 /* Close the extra descriptor for the pseudo tty. */
564 close(ttyfd);
565
566///XXXX ? move to do_child() ??
567 /*
568 * Get IP address of client. This is needed because we want
569 * to record where the user logged in from. If the
570 * connection is not a socket, let the ip address be 0.0.0.0.
571 */
572 memset(&from, 0, sizeof(from));
573 if (packet_connection_is_on_socket()) {
574 fromlen = sizeof(from);
575 if (getpeername(packet_get_connection_in(),
576 (struct sockaddr *) & from, &fromlen) < 0) {
577 debug("getpeername: %.100s", strerror(errno));
578 fatal_cleanup();
579 }
580 }
581 /* Record that there was a login on that terminal. */
582 record_login(pid, s->tty, pw->pw_name, pw->pw_uid, hostname,
583 (struct sockaddr *)&from);
584
585 /* Check if .hushlogin exists. */
586 snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
587 quiet_login = stat(line, &st) >= 0;
588
589#ifdef USE_PAM
590 if (!quiet_login)
591 print_pam_messages();
592#endif /* USE_PAM */
593
594 /*
595 * If the user has logged in before, display the time of last
596 * login. However, don't display anything extra if a command
597 * has been specified (so that ssh can be used to execute
598 * commands on a remote machine without users knowing they
599 * are going to another machine). Login(1) will do this for
600 * us as well, so check if login(1) is used
601 */
602 if (command == NULL && last_login_time != 0 && !quiet_login &&
603 !options.use_login) {
604 /* Convert the date to a string. */
605 time_string = ctime(&last_login_time);
606 /* Remove the trailing newline. */
607 if (strchr(time_string, '\n'))
608 *strchr(time_string, '\n') = 0;
609 /* Display the last login time. Host if displayed
610 if known. */
611 if (strcmp(buf, "") == 0)
612 printf("Last login: %s\r\n", time_string);
613 else
614 printf("Last login: %s from %s\r\n", time_string, buf);
615 }
616 /*
617 * Print /etc/motd unless a command was specified or printing
618 * it was disabled in server options or login(1) will be
619 * used. Note that some machines appear to print it in
620 * /etc/profile or similar.
621 */
622 if (command == NULL && options.print_motd && !quiet_login &&
623 !options.use_login) {
624 /* Print /etc/motd if it exists. */
625 f = fopen("/etc/motd", "r");
626 if (f) {
627 while (fgets(line, sizeof(line), f))
628 fputs(line, stdout);
629 fclose(f);
630 }
631 }
632 /* Do common processing for the child, such as execing the command. */
633 do_child(command, pw, s->term, s->display, s->auth_proto, s->auth_data, s->tty);
634 /* NOTREACHED */
635 }
636 if (pid < 0)
637 packet_disconnect("fork failed: %.100s", strerror(errno));
638 s->pid = pid;
639
640 /* Parent. Close the slave side of the pseudo tty. */
641 close(ttyfd);
642
643 /*
644 * Create another descriptor of the pty master side for use as the
645 * standard input. We could use the original descriptor, but this
646 * simplifies code in server_loop. The descriptor is bidirectional.
647 */
648 fdout = dup(ptyfd);
649 if (fdout < 0)
650 packet_disconnect("dup #1 failed: %.100s", strerror(errno));
651
652 /* we keep a reference to the pty master */
653 ptymaster = dup(ptyfd);
654 if (ptymaster < 0)
655 packet_disconnect("dup #2 failed: %.100s", strerror(errno));
656 s->ptymaster = ptymaster;
657
658 /* Enter interactive session. */
Damien Millerefb4afe2000-04-12 18:45:05 +1000659 if (compat20) {
660 session_set_fds(s, ptyfd, fdout, -1);
661 } else {
662 server_loop(pid, ptyfd, fdout, -1);
663 /* server_loop _has_ closed ptyfd and fdout. */
664 session_pty_cleanup(s);
665 }
Damien Millerb38eff82000-04-01 11:09:21 +1000666}
667
668/*
669 * Sets the value of the given variable in the environment. If the variable
670 * already exists, its value is overriden.
671 */
Damien Miller4af51302000-04-16 11:18:38 +1000672void
Damien Millerb38eff82000-04-01 11:09:21 +1000673child_set_env(char ***envp, unsigned int *envsizep, const char *name,
674 const char *value)
675{
676 unsigned int i, namelen;
677 char **env;
678
679 /*
680 * Find the slot where the value should be stored. If the variable
681 * already exists, we reuse the slot; otherwise we append a new slot
682 * at the end of the array, expanding if necessary.
683 */
684 env = *envp;
685 namelen = strlen(name);
686 for (i = 0; env[i]; i++)
687 if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
688 break;
689 if (env[i]) {
690 /* Reuse the slot. */
691 xfree(env[i]);
692 } else {
693 /* New variable. Expand if necessary. */
694 if (i >= (*envsizep) - 1) {
695 (*envsizep) += 50;
696 env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
697 }
698 /* Need to set the NULL pointer at end of array beyond the new slot. */
699 env[i + 1] = NULL;
700 }
701
702 /* Allocate space and format the variable in the appropriate slot. */
703 env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
704 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
705}
706
707/*
708 * Reads environment variables from the given file and adds/overrides them
709 * into the environment. If the file does not exist, this does nothing.
710 * Otherwise, it must consist of empty lines, comments (line starts with '#')
711 * and assignments of the form name=value. No other forms are allowed.
712 */
Damien Miller4af51302000-04-16 11:18:38 +1000713void
Damien Millerb38eff82000-04-01 11:09:21 +1000714read_environment_file(char ***env, unsigned int *envsize,
715 const char *filename)
716{
717 FILE *f;
718 char buf[4096];
719 char *cp, *value;
720
721 f = fopen(filename, "r");
722 if (!f)
723 return;
724
725 while (fgets(buf, sizeof(buf), f)) {
726 for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
727 ;
728 if (!*cp || *cp == '#' || *cp == '\n')
729 continue;
730 if (strchr(cp, '\n'))
731 *strchr(cp, '\n') = '\0';
732 value = strchr(cp, '=');
733 if (value == NULL) {
734 fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
735 continue;
736 }
737 /* Replace the equals sign by nul, and advance value to the value string. */
738 *value = '\0';
739 value++;
740 child_set_env(env, envsize, cp, value);
741 }
742 fclose(f);
743}
744
745#ifdef USE_PAM
746/*
747 * Sets any environment variables which have been specified by PAM
748 */
749void do_pam_environment(char ***env, int *envsize)
750{
751 char *equals, var_name[512], var_val[512];
752 char **pam_env;
753 int i;
754
755 if ((pam_env = fetch_pam_environment()) == NULL)
756 return;
757
758 for(i = 0; pam_env[i] != NULL; i++) {
759 if ((equals = strstr(pam_env[i], "=")) == NULL)
760 continue;
761
762 if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) {
763 memset(var_name, '\0', sizeof(var_name));
764 memset(var_val, '\0', sizeof(var_val));
765
766 strncpy(var_name, pam_env[i], equals - pam_env[i]);
767 strcpy(var_val, equals + 1);
768
769 debug("PAM environment: %s=%s", var_name, var_val);
770
771 child_set_env(env, envsize, var_name, var_val);
772 }
773 }
774}
775#endif /* USE_PAM */
776
777/*
778 * Performs common processing for the child, such as setting up the
779 * environment, closing extra file descriptors, setting the user and group
780 * ids, and executing the command or shell.
781 */
Damien Miller4af51302000-04-16 11:18:38 +1000782void
Damien Millerb38eff82000-04-01 11:09:21 +1000783do_child(const char *command, struct passwd * pw, const char *term,
784 const char *display, const char *auth_proto,
785 const char *auth_data, const char *ttyname)
786{
787 const char *shell, *cp = NULL;
788 char buf[256];
789 FILE *f;
790 unsigned int envsize, i;
791 char **env;
792 extern char **environ;
793 struct stat st;
794 char *argv[10];
795
796#ifndef USE_PAM /* pam_nologin handles this */
797 f = fopen("/etc/nologin", "r");
798 if (f) {
799 /* /etc/nologin exists. Print its contents and exit. */
800 while (fgets(buf, sizeof(buf), f))
801 fputs(buf, stderr);
802 fclose(f);
803 if (pw->pw_uid != 0)
804 exit(254);
805 }
806#endif /* USE_PAM */
807
808 /* Set login name in the kernel. */
809 if (setlogin(pw->pw_name) < 0)
810 error("setlogin failed: %s", strerror(errno));
811
812 /* Set uid, gid, and groups. */
813 /* Login(1) does this as well, and it needs uid 0 for the "-h"
814 switch, so we let login(1) to this for us. */
815 if (!options.use_login) {
816 if (getuid() == 0 || geteuid() == 0) {
817 if (setgid(pw->pw_gid) < 0) {
818 perror("setgid");
819 exit(1);
820 }
821 /* Initialize the group list. */
822 if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
823 perror("initgroups");
824 exit(1);
825 }
826 endgrent();
827
828 /* Permanently switch to the desired uid. */
829 permanently_set_uid(pw->pw_uid);
830 }
831 if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
832 fatal("Failed to set uids to %d.", (int) pw->pw_uid);
833 }
834 /*
835 * Get the shell from the password data. An empty shell field is
836 * legal, and means /bin/sh.
837 */
838 shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
839
840#ifdef AFS
841 /* Try to get AFS tokens for the local cell. */
842 if (k_hasafs()) {
843 char cell[64];
844
845 if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
846 krb_afslog(cell, 0);
847
848 krb_afslog(0, 0);
849 }
850#endif /* AFS */
851
852 /* Initialize the environment. */
853 envsize = 100;
854 env = xmalloc(envsize * sizeof(char *));
855 env[0] = NULL;
856
857 if (!options.use_login) {
858 /* Set basic environment. */
859 child_set_env(&env, &envsize, "USER", pw->pw_name);
860 child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
861 child_set_env(&env, &envsize, "HOME", pw->pw_dir);
862 child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
863
864 snprintf(buf, sizeof buf, "%.200s/%.50s",
865 _PATH_MAILDIR, pw->pw_name);
866 child_set_env(&env, &envsize, "MAIL", buf);
867
868 /* Normal systems set SHELL by default. */
869 child_set_env(&env, &envsize, "SHELL", shell);
870 }
871 if (getenv("TZ"))
872 child_set_env(&env, &envsize, "TZ", getenv("TZ"));
873
874 /* Set custom environment options from RSA authentication. */
875 while (custom_environment) {
876 struct envstring *ce = custom_environment;
877 char *s = ce->s;
878 int i;
879 for (i = 0; s[i] != '=' && s[i]; i++);
880 if (s[i] == '=') {
881 s[i] = 0;
882 child_set_env(&env, &envsize, s, s + i + 1);
883 }
884 custom_environment = ce->next;
885 xfree(ce->s);
886 xfree(ce);
887 }
888
889 snprintf(buf, sizeof buf, "%.50s %d %d",
890 get_remote_ipaddr(), get_remote_port(), get_local_port());
891 child_set_env(&env, &envsize, "SSH_CLIENT", buf);
892
893 if (ttyname)
894 child_set_env(&env, &envsize, "SSH_TTY", ttyname);
895 if (term)
896 child_set_env(&env, &envsize, "TERM", term);
897 if (display)
898 child_set_env(&env, &envsize, "DISPLAY", display);
899
900#ifdef _AIX
901 {
902 char *authstate,*krb5cc;
903
904 if ((authstate = getenv("AUTHSTATE")) != NULL)
905 child_set_env(&env,&envsize,"AUTHSTATE",authstate);
906
907 if ((krb5cc = getenv("KRB5CCNAME")) != NULL)
908 child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc);
909 }
910#endif
911
912#ifdef KRB4
913 {
914 extern char *ticket;
915
916 if (ticket)
917 child_set_env(&env, &envsize, "KRBTKFILE", ticket);
918 }
919#endif /* KRB4 */
920
921#ifdef USE_PAM
922 /* Pull in any environment variables that may have been set by PAM. */
923 do_pam_environment(&env, &envsize);
924#endif /* USE_PAM */
925
926 read_environment_file(&env,&envsize,"/etc/environment");
927
928 if (xauthfile)
929 child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
930 if (auth_get_socket_name() != NULL)
931 child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
932 auth_get_socket_name());
933
934 /* read $HOME/.ssh/environment. */
935 if (!options.use_login) {
936 snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
937 read_environment_file(&env, &envsize, buf);
938 }
939 if (debug_flag) {
940 /* dump the environment */
941 fprintf(stderr, "Environment:\n");
942 for (i = 0; env[i]; i++)
943 fprintf(stderr, " %.200s\n", env[i]);
944 }
945 /*
946 * Close the connection descriptors; note that this is the child, and
947 * the server will still have the socket open, and it is important
948 * that we do not shutdown it. Note that the descriptors cannot be
949 * closed before building the environment, as we call
950 * get_remote_ipaddr there.
951 */
952 if (packet_get_connection_in() == packet_get_connection_out())
953 close(packet_get_connection_in());
954 else {
955 close(packet_get_connection_in());
956 close(packet_get_connection_out());
957 }
958 /*
959 * Close all descriptors related to channels. They will still remain
960 * open in the parent.
961 */
962 /* XXX better use close-on-exec? -markus */
963 channel_close_all();
964
965 /*
966 * Close any extra file descriptors. Note that there may still be
967 * descriptors left by system functions. They will be closed later.
968 */
969 endpwent();
970
971 /*
972 * Close any extra open file descriptors so that we don\'t have them
973 * hanging around in clients. Note that we want to do this after
974 * initgroups, because at least on Solaris 2.3 it leaves file
975 * descriptors open.
976 */
977 for (i = 3; i < 64; i++)
978 close(i);
979
980 /* Change current directory to the user\'s home directory. */
981 if (chdir(pw->pw_dir) < 0)
982 fprintf(stderr, "Could not chdir to home directory %s: %s\n",
983 pw->pw_dir, strerror(errno));
984
985 /*
986 * Must take new environment into use so that .ssh/rc, /etc/sshrc and
987 * xauth are run in the proper environment.
988 */
989 environ = env;
990
991 /*
992 * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
993 * in this order).
994 */
995 if (!options.use_login) {
996 if (stat(SSH_USER_RC, &st) >= 0) {
997 if (debug_flag)
998 fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
999
1000 f = popen("/bin/sh " SSH_USER_RC, "w");
1001 if (f) {
1002 if (auth_proto != NULL && auth_data != NULL)
1003 fprintf(f, "%s %s\n", auth_proto, auth_data);
1004 pclose(f);
1005 } else
1006 fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
1007 } else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
1008 if (debug_flag)
1009 fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
1010
1011 f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
1012 if (f) {
1013 if (auth_proto != NULL && auth_data != NULL)
1014 fprintf(f, "%s %s\n", auth_proto, auth_data);
1015 pclose(f);
1016 } else
1017 fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
1018 }
1019#ifdef XAUTH_PATH
1020 else {
1021 /* Add authority data to .Xauthority if appropriate. */
1022 if (auth_proto != NULL && auth_data != NULL) {
1023 if (debug_flag)
1024 fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
1025 XAUTH_PATH, display, auth_proto, auth_data);
1026
1027 f = popen(XAUTH_PATH " -q -", "w");
1028 if (f) {
1029 fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
1030 pclose(f);
1031 } else
1032 fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
1033 }
1034 }
1035#endif /* XAUTH_PATH */
1036
1037 /* Get the last component of the shell name. */
1038 cp = strrchr(shell, '/');
1039 if (cp)
1040 cp++;
1041 else
1042 cp = shell;
1043 }
1044 /*
1045 * If we have no command, execute the shell. In this case, the shell
1046 * name to be passed in argv[0] is preceded by '-' to indicate that
1047 * this is a login shell.
1048 */
1049 if (!command) {
1050 if (!options.use_login) {
1051 char buf[256];
1052
1053 /*
1054 * Check for mail if we have a tty and it was enabled
1055 * in server options.
1056 */
1057 if (ttyname && options.check_mail) {
1058 char *mailbox;
1059 struct stat mailstat;
1060 mailbox = getenv("MAIL");
1061 if (mailbox != NULL) {
1062 if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0)
1063 printf("No mail.\n");
1064 else if (mailstat.st_mtime < mailstat.st_atime)
1065 printf("You have mail.\n");
1066 else
1067 printf("You have new mail.\n");
1068 }
1069 }
1070 /* Start the shell. Set initial character to '-'. */
1071 buf[0] = '-';
1072 strncpy(buf + 1, cp, sizeof(buf) - 1);
1073 buf[sizeof(buf) - 1] = 0;
1074
1075 /* Execute the shell. */
1076 argv[0] = buf;
1077 argv[1] = NULL;
1078 execve(shell, argv, env);
1079
1080 /* Executing the shell failed. */
1081 perror(shell);
1082 exit(1);
1083
1084 } else {
1085 /* Launch login(1). */
1086
1087 execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(),
1088 "-p", "-f", "--", pw->pw_name, NULL);
1089
1090 /* Login couldn't be executed, die. */
1091
1092 perror("login");
1093 exit(1);
1094 }
1095 }
1096 /*
1097 * Execute the command using the user's shell. This uses the -c
1098 * option to execute the command.
1099 */
1100 argv[0] = (char *) cp;
1101 argv[1] = "-c";
1102 argv[2] = (char *) command;
1103 argv[3] = NULL;
1104 execve(shell, argv, env);
1105 perror(shell);
1106 exit(1);
1107}
1108
1109Session *
1110session_new(void)
1111{
1112 int i;
1113 static int did_init = 0;
1114 if (!did_init) {
1115 debug("session_new: init");
1116 for(i = 0; i < MAX_SESSIONS; i++) {
1117 sessions[i].used = 0;
1118 sessions[i].self = i;
1119 }
1120 did_init = 1;
1121 }
1122 for(i = 0; i < MAX_SESSIONS; i++) {
1123 Session *s = &sessions[i];
1124 if (! s->used) {
1125 s->pid = 0;
Damien Millerbd483e72000-04-30 10:00:53 +10001126 s->extended = 0;
Damien Millerb38eff82000-04-01 11:09:21 +10001127 s->chanid = -1;
1128 s->ptyfd = -1;
1129 s->ttyfd = -1;
1130 s->term = NULL;
1131 s->pw = NULL;
1132 s->display = NULL;
1133 s->screen = 0;
1134 s->auth_data = NULL;
1135 s->auth_proto = NULL;
1136 s->used = 1;
Damien Millerbd483e72000-04-30 10:00:53 +10001137 s->pw = NULL;
Damien Millerb38eff82000-04-01 11:09:21 +10001138 debug("session_new: session %d", i);
1139 return s;
1140 }
1141 }
1142 return NULL;
1143}
1144
1145void
1146session_dump(void)
1147{
1148 int i;
1149 for(i = 0; i < MAX_SESSIONS; i++) {
1150 Session *s = &sessions[i];
1151 debug("dump: used %d session %d %p channel %d pid %d",
1152 s->used,
1153 s->self,
1154 s,
1155 s->chanid,
1156 s->pid);
1157 }
1158}
1159
Damien Millerefb4afe2000-04-12 18:45:05 +10001160int
1161session_open(int chanid)
1162{
1163 Session *s = session_new();
1164 debug("session_open: channel %d", chanid);
1165 if (s == NULL) {
1166 error("no more sessions");
1167 return 0;
1168 }
Damien Millerefb4afe2000-04-12 18:45:05 +10001169 s->pw = auth_get_user();
1170 if (s->pw == NULL)
Damien Millerbd483e72000-04-30 10:00:53 +10001171 fatal("no user for session %i", s->self);
1172 debug("session_open: session %d: link with channel %d", s->self, chanid);
1173 s->chanid = chanid;
Damien Millerefb4afe2000-04-12 18:45:05 +10001174 return 1;
1175}
1176
1177Session *
1178session_by_channel(int id)
1179{
1180 int i;
1181 for(i = 0; i < MAX_SESSIONS; i++) {
1182 Session *s = &sessions[i];
1183 if (s->used && s->chanid == id) {
1184 debug("session_by_channel: session %d channel %d", i, id);
1185 return s;
1186 }
1187 }
1188 debug("session_by_channel: unknown channel %d", id);
1189 session_dump();
1190 return NULL;
1191}
1192
1193Session *
1194session_by_pid(pid_t pid)
1195{
1196 int i;
1197 debug("session_by_pid: pid %d", pid);
1198 for(i = 0; i < MAX_SESSIONS; i++) {
1199 Session *s = &sessions[i];
1200 if (s->used && s->pid == pid)
1201 return s;
1202 }
1203 error("session_by_pid: unknown pid %d", pid);
1204 session_dump();
1205 return NULL;
1206}
1207
1208int
1209session_window_change_req(Session *s)
1210{
1211 s->col = packet_get_int();
1212 s->row = packet_get_int();
1213 s->xpixel = packet_get_int();
1214 s->ypixel = packet_get_int();
Damien Miller4af51302000-04-16 11:18:38 +10001215 packet_done();
Damien Millerefb4afe2000-04-12 18:45:05 +10001216 pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
1217 return 1;
1218}
1219
1220int
1221session_pty_req(Session *s)
1222{
1223 unsigned int len;
Damien Miller4af51302000-04-16 11:18:38 +10001224 char *term_modes; /* encoded terminal modes */
Damien Millerefb4afe2000-04-12 18:45:05 +10001225
1226 if (s->ttyfd != -1)
Damien Miller4af51302000-04-16 11:18:38 +10001227 return 0;
Damien Millerefb4afe2000-04-12 18:45:05 +10001228 s->term = packet_get_string(&len);
1229 s->col = packet_get_int();
1230 s->row = packet_get_int();
1231 s->xpixel = packet_get_int();
1232 s->ypixel = packet_get_int();
Damien Miller4af51302000-04-16 11:18:38 +10001233 term_modes = packet_get_string(&len);
1234 packet_done();
Damien Millerefb4afe2000-04-12 18:45:05 +10001235
1236 if (strcmp(s->term, "") == 0) {
1237 xfree(s->term);
1238 s->term = NULL;
1239 }
1240 /* Allocate a pty and open it. */
1241 if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
1242 xfree(s->term);
1243 s->term = NULL;
1244 s->ptyfd = -1;
1245 s->ttyfd = -1;
1246 error("session_pty_req: session %d alloc failed", s->self);
Damien Miller4af51302000-04-16 11:18:38 +10001247 xfree(term_modes);
1248 return 0;
Damien Millerefb4afe2000-04-12 18:45:05 +10001249 }
1250 debug("session_pty_req: session %d alloc %s", s->self, s->tty);
1251 /*
1252 * Add a cleanup function to clear the utmp entry and record logout
1253 * time in case we call fatal() (e.g., the connection gets closed).
1254 */
1255 fatal_add_cleanup(pty_cleanup_proc, (void *)s);
1256 pty_setowner(s->pw, s->tty);
1257 /* Get window size from the packet. */
1258 pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
1259
Damien Miller5f056372000-04-16 12:31:48 +10001260 /* XXX parse and set terminal modes */
1261 xfree(term_modes);
Damien Millerefb4afe2000-04-12 18:45:05 +10001262 return 1;
1263}
1264
Damien Millerbd483e72000-04-30 10:00:53 +10001265int
1266session_subsystem_req(Session *s)
1267{
1268 unsigned int len;
1269 int success = 0;
1270 char *subsys = packet_get_string(&len);
1271
1272 packet_done();
1273 log("subsystem request for %s", subsys);
1274
1275 xfree(subsys);
1276 return success;
1277}
1278
1279int
1280session_x11_req(Session *s)
1281{
1282 if (!options.x11_forwarding) {
1283 debug("X11 forwarding disabled in server configuration file.");
1284 return 0;
1285 }
1286 if (xauthfile != NULL) {
1287 debug("X11 fwd already started.");
1288 return 0;
1289 }
1290
1291 debug("Received request for X11 forwarding with auth spoofing.");
1292 if (s->display != NULL)
1293 packet_disconnect("Protocol error: X11 display already set.");
1294
1295 s->single_connection = packet_get_char();
1296 s->auth_proto = packet_get_string(NULL);
1297 s->auth_data = packet_get_string(NULL);
1298 s->screen = packet_get_int();
1299 packet_done();
1300
1301 s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
1302 if (s->display == NULL) {
1303 xfree(s->auth_proto);
1304 xfree(s->auth_data);
1305 return 0;
1306 }
1307 xauthfile = xmalloc(MAXPATHLEN);
1308 strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
1309 temporarily_use_uid(s->pw->pw_uid);
1310 if (mkdtemp(xauthfile) == NULL) {
1311 restore_uid();
1312 error("private X11 dir: mkdtemp %s failed: %s",
1313 xauthfile, strerror(errno));
1314 xfree(xauthfile);
1315 xauthfile = NULL;
1316 xfree(s->auth_proto);
1317 xfree(s->auth_data);
1318 /* XXXX remove listening channels */
1319 return 0;
1320 }
1321 strlcat(xauthfile, "/cookies", MAXPATHLEN);
1322 open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
1323 restore_uid();
1324 fatal_add_cleanup(xauthfile_cleanup_proc, s);
1325 return 1;
1326}
1327
Damien Millerefb4afe2000-04-12 18:45:05 +10001328void
1329session_input_channel_req(int id, void *arg)
1330{
1331 unsigned int len;
1332 int reply;
1333 int success = 0;
1334 char *rtype;
1335 Session *s;
1336 Channel *c;
1337
1338 rtype = packet_get_string(&len);
1339 reply = packet_get_char();
1340
1341 s = session_by_channel(id);
1342 if (s == NULL)
1343 fatal("session_input_channel_req: channel %d: no session", id);
1344 c = channel_lookup(id);
1345 if (c == NULL)
1346 fatal("session_input_channel_req: channel %d: bad channel", id);
1347
1348 debug("session_input_channel_req: session %d channel %d request %s reply %d",
1349 s->self, id, rtype, reply);
1350
1351 /*
1352 * a session is in LARVAL state until a shell
1353 * or programm is executed
1354 */
1355 if (c->type == SSH_CHANNEL_LARVAL) {
1356 if (strcmp(rtype, "shell") == 0) {
Damien Miller1b26ab22000-04-30 10:12:49 +10001357 packet_done();
1358 s->extended = 1;
Damien Millerefb4afe2000-04-12 18:45:05 +10001359 if (s->ttyfd == -1)
1360 do_exec_no_pty(s, NULL, s->pw);
1361 else
1362 do_exec_pty(s, NULL, s->pw);
1363 success = 1;
1364 } else if (strcmp(rtype, "exec") == 0) {
1365 char *command = packet_get_string(&len);
Damien Miller5f056372000-04-16 12:31:48 +10001366 packet_done();
Damien Millerbd483e72000-04-30 10:00:53 +10001367 s->extended = 1;
Damien Millerefb4afe2000-04-12 18:45:05 +10001368 if (s->ttyfd == -1)
1369 do_exec_no_pty(s, command, s->pw);
1370 else
1371 do_exec_pty(s, command, s->pw);
1372 xfree(command);
1373 success = 1;
1374 } else if (strcmp(rtype, "pty-req") == 0) {
Damien Miller5f056372000-04-16 12:31:48 +10001375 success = session_pty_req(s);
Damien Millerbd483e72000-04-30 10:00:53 +10001376 } else if (strcmp(rtype, "x11-req") == 0) {
1377 success = session_x11_req(s);
1378 } else if (strcmp(rtype, "subsystem") == 0) {
1379 success = session_subsystem_req(s);
Damien Millerefb4afe2000-04-12 18:45:05 +10001380 }
1381 }
1382 if (strcmp(rtype, "window-change") == 0) {
1383 success = session_window_change_req(s);
1384 }
1385
1386 if (reply) {
1387 packet_start(success ?
1388 SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
1389 packet_put_int(c->remote_id);
1390 packet_send();
1391 }
1392 xfree(rtype);
1393}
1394
1395void
1396session_set_fds(Session *s, int fdin, int fdout, int fderr)
1397{
1398 if (!compat20)
1399 fatal("session_set_fds: called for proto != 2.0");
1400 /*
1401 * now that have a child and a pipe to the child,
1402 * we can activate our channel and register the fd's
1403 */
1404 if (s->chanid == -1)
1405 fatal("no channel for session %d", s->self);
1406 channel_set_fds(s->chanid,
1407 fdout, fdin, fderr,
1408 fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ);
1409}
1410
Damien Millerb38eff82000-04-01 11:09:21 +10001411void
1412session_pty_cleanup(Session *s)
1413{
1414 if (s == NULL || s->ttyfd == -1)
1415 return;
1416
1417 debug("session_pty_cleanup: session %i release %s", s->self, s->tty);
1418
1419 /* Cancel the cleanup function. */
1420 fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
1421
1422 /* Record that the user has logged out. */
1423 record_logout(s->pid, s->tty);
1424
1425 /* Release the pseudo-tty. */
1426 pty_release(s->tty);
1427
1428 /*
1429 * Close the server side of the socket pairs. We must do this after
1430 * the pty cleanup, so that another process doesn't get this pty
1431 * while we're still cleaning up.
1432 */
1433 if (close(s->ptymaster) < 0)
1434 error("close(s->ptymaster): %s", strerror(errno));
1435}
Damien Millerefb4afe2000-04-12 18:45:05 +10001436
1437void
1438session_exit_message(Session *s, int status)
1439{
1440 Channel *c;
1441 if (s == NULL)
1442 fatal("session_close: no session");
1443 c = channel_lookup(s->chanid);
1444 if (c == NULL)
1445 fatal("session_close: session %d: no channel %d",
1446 s->self, s->chanid);
1447 debug("session_exit_message: session %d channel %d pid %d",
1448 s->self, s->chanid, s->pid);
1449
1450 if (WIFEXITED(status)) {
1451 channel_request_start(s->chanid,
1452 "exit-status", 0);
1453 packet_put_int(WEXITSTATUS(status));
1454 packet_send();
1455 } else if (WIFSIGNALED(status)) {
1456 channel_request_start(s->chanid,
1457 "exit-signal", 0);
1458 packet_put_int(WTERMSIG(status));
1459 packet_put_char(WCOREDUMP(status));
1460 packet_put_cstring("");
1461 packet_put_cstring("");
1462 packet_send();
1463 } else {
1464 /* Some weird exit cause. Just exit. */
1465 packet_disconnect("wait returned status %04x.", status);
1466 }
1467
1468 /* disconnect channel */
1469 debug("session_exit_message: release channel %d", s->chanid);
1470 channel_cancel_cleanup(s->chanid);
Damien Miller166fca82000-04-20 07:42:21 +10001471 /*
1472 * emulate a write failure with 'chan_write_failed', nobody will be
1473 * interested in data we write.
1474 * Note that we must not call 'chan_read_failed', since there could
1475 * be some more data waiting in the pipe.
1476 */
Damien Millerbd483e72000-04-30 10:00:53 +10001477 if (c->ostate != CHAN_OUTPUT_CLOSED)
1478 chan_write_failed(c);
Damien Millerefb4afe2000-04-12 18:45:05 +10001479 s->chanid = -1;
1480}
1481
1482void
1483session_free(Session *s)
1484{
1485 debug("session_free: session %d pid %d", s->self, s->pid);
1486 if (s->term)
1487 xfree(s->term);
1488 if (s->display)
1489 xfree(s->display);
1490 if (s->auth_data)
1491 xfree(s->auth_data);
1492 if (s->auth_proto)
1493 xfree(s->auth_proto);
1494 s->used = 0;
1495}
1496
1497void
1498session_close(Session *s)
1499{
1500 session_pty_cleanup(s);
1501 session_free(s);
1502}
1503
1504void
1505session_close_by_pid(pid_t pid, int status)
1506{
1507 Session *s = session_by_pid(pid);
1508 if (s == NULL) {
1509 debug("session_close_by_pid: no session for pid %d", s->pid);
1510 return;
1511 }
1512 if (s->chanid != -1)
1513 session_exit_message(s, status);
1514 session_close(s);
1515}
1516
1517/*
1518 * this is called when a channel dies before
1519 * the session 'child' itself dies
1520 */
1521void
1522session_close_by_channel(int id, void *arg)
1523{
1524 Session *s = session_by_channel(id);
1525 if (s == NULL) {
1526 debug("session_close_by_channel: no session for channel %d", id);
1527 return;
1528 }
1529 /* disconnect channel */
1530 channel_cancel_cleanup(s->chanid);
1531 s->chanid = -1;
1532
1533 debug("session_close_by_channel: channel %d kill %d", id, s->pid);
1534 if (s->pid == 0) {
1535 /* close session immediately */
1536 session_close(s);
1537 } else {
1538 /* notify child, delay session cleanup */
1539 if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
1540 error("session_close_by_channel: kill %d: %s",
1541 s->pid, strerror(errno));
1542 }
1543}
1544
1545void
1546do_authenticated2(void)
1547{
1548 /*
1549 * Cancel the alarm we set to limit the time taken for
1550 * authentication.
1551 */
1552 alarm(0);
Damien Millerefb4afe2000-04-12 18:45:05 +10001553 server_loop2();
Damien Miller1b26ab22000-04-30 10:12:49 +10001554 if (xauthfile)
1555 xauthfile_cleanup_proc(NULL);
Damien Millerefb4afe2000-04-12 18:45:05 +10001556}