+/* $OpenBSD: serverloop.c,v 1.145 2006/10/11 12:38:03 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
*/
#include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.118 2005/07/17 07:17:55 djm Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdarg.h>
#include "xmalloc.h"
#include "packet.h"
#include "compat.h"
#include "ssh1.h"
#include "ssh2.h"
+#include "key.h"
+#include "cipher.h"
+#include "kex.h"
+#include "hostfile.h"
#include "auth.h"
#include "session.h"
#include "dispatch.h"
#include "auth-options.h"
#include "serverloop.h"
#include "misc.h"
-#include "kex.h"
extern ServerOptions options;
/* XXX */
extern Kex *xxx_kex;
extern Authctxt *the_authctxt;
+extern int use_privsep;
static Buffer stdin_buffer; /* Buffer for stdin data. */
static Buffer stdout_buffer; /* Buffer for stdout data. */
static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */
+/* Cleanup on signals (!use_privsep case only) */
+static volatile sig_atomic_t received_sigterm = 0;
+
/* prototypes */
static void server_init_dispatch(void);
debug2("notify_done: reading");
}
+/*ARGSUSED*/
static void
sigchld_handler(int sig)
{
int save_errno = errno;
- debug("Received SIGCHLD.");
child_terminated = 1;
#ifndef _UNICOS
mysignal(SIGCHLD, sigchld_handler);
errno = save_errno;
}
+/*ARGSUSED*/
+static void
+sigterm_handler(int sig)
+{
+ received_sigterm = sig;
+}
+
/*
* Make packets from buffered stderr data, and buffer it for sending
* to the client.
int channel_id;
/* timeout, check to see how many we have had */
- if (++client_alive_timeouts > options.client_alive_count_max)
- packet_disconnect("Timeout, your session not responding.");
+ if (++client_alive_timeouts > options.client_alive_count_max) {
+ logit("Timeout, client not responding.");
+ cleanup_exit(255);
+ }
/*
* send a bogus global/channel request with "wantreply",
struct timeval tv, *tvp;
int ret;
int client_alive_scheduled = 0;
+ int program_alive_scheduled = 0;
/*
* if using client_alive, set the max timeout accordingly,
* the client, try to get some more data from the program.
*/
if (packet_not_very_much_data_to_write()) {
+ program_alive_scheduled = child_terminated;
if (!fdout_eof)
FD_SET(fdout, *readsetp);
if (!fderr_eof)
memset(*writesetp, 0, *nallocp);
if (errno != EINTR)
error("select: %.100s", strerror(errno));
- } else if (ret == 0 && client_alive_scheduled)
- client_alive_check();
+ } else {
+ if (ret == 0 && client_alive_scheduled)
+ client_alive_check();
+ if (!compat20 && program_alive_scheduled && fdin_is_tty) {
+ if (!fdout_eof)
+ FD_SET(fdout, *readsetp);
+ if (!fderr_eof)
+ FD_SET(fderr, *readsetp);
+ }
+ }
notify_done(*readsetp);
}
* in buffers and processed later.
*/
static void
-process_input(fd_set * readset)
+process_input(fd_set *readset)
{
int len;
char buf[16384];
/* Read and buffer any available stdout data from the program. */
if (!fdout_eof && FD_ISSET(fdout, readset)) {
+ errno = 0;
len = read(fdout, buf, sizeof(buf));
- if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+ if (len < 0 && (errno == EINTR ||
+ (errno == EAGAIN && !child_terminated))) {
/* do nothing */
+#ifndef PTY_ZEROREAD
} else if (len <= 0) {
+#else
+ } else if ((!isatty(fdout) && len <= 0) ||
+ (isatty(fdout) && (len < 0 || (len == 0 && errno != 0)))) {
+#endif
fdout_eof = 1;
} else {
buffer_append(&stdout_buffer, buf, len);
}
/* Read and buffer any available stderr data from the program. */
if (!fderr_eof && FD_ISSET(fderr, readset)) {
+ errno = 0;
len = read(fderr, buf, sizeof(buf));
- if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+ if (len < 0 && (errno == EINTR ||
+ (errno == EAGAIN && !child_terminated))) {
/* do nothing */
+#ifndef PTY_ZEROREAD
} else if (len <= 0) {
+#else
+ } else if ((!isatty(fderr) && len <= 0) ||
+ (isatty(fderr) && (len < 0 || (len == 0 && errno != 0)))) {
+#endif
fderr_eof = 1;
} else {
buffer_append(&stderr_buffer, buf, len);
* Sends data from internal buffers to client program stdin.
*/
static void
-process_output(fd_set * writeset)
+process_output(fd_set *writeset)
{
struct termios tio;
u_char *data;
child_terminated = 0;
mysignal(SIGCHLD, sigchld_handler);
+ if (!use_privsep) {
+ signal(SIGTERM, sigterm_handler);
+ signal(SIGINT, sigterm_handler);
+ signal(SIGQUIT, sigterm_handler);
+ }
+
/* Initialize our global variables. */
fdin = fdin_arg;
fdout = fdout_arg;
* If we have no separate fderr (which is the case when we have a pty
* - there we cannot make difference between data sent to stdout and
* stderr), indicate that we have seen an EOF from stderr. This way
- * we don\'t need to check the descriptor everywhere.
+ * we don't need to check the descriptor everywhere.
*/
if (fderr == -1)
fderr_eof = 1;
wait_until_can_do_something(&readset, &writeset, &max_fd,
&nalloc, max_time_milliseconds);
+ if (received_sigterm) {
+ logit("Exiting on signal %d", received_sigterm);
+ /* Clean up sessions, utmp, etc. */
+ cleanup_exit(255);
+ }
+
/* Process any channel events. */
channel_after_select(readset, writeset);
sigaddset(&nset, SIGCHLD);
sigprocmask(SIG_BLOCK, &nset, &oset);
if (child_terminated) {
+ debug("Received SIGCHLD.");
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
(pid < 0 && errno == EINTR))
if (pid > 0)
connection_in = packet_get_connection_in();
connection_out = packet_get_connection_out();
+ if (!use_privsep) {
+ signal(SIGTERM, sigterm_handler);
+ signal(SIGINT, sigterm_handler);
+ signal(SIGQUIT, sigterm_handler);
+ }
+
notify_setup();
max_fd = MAX(connection_in, connection_out);
wait_until_can_do_something(&readset, &writeset, &max_fd,
&nalloc, 0);
+ if (received_sigterm) {
+ logit("Exiting on signal %d", received_sigterm);
+ /* Clean up sessions, utmp, etc. */
+ cleanup_exit(255);
+ }
+
collect_children();
if (!rekeying) {
channel_after_select(readset, writeset);
static void
server_input_window_size(int type, u_int32_t seq, void *ctxt)
{
- int row = packet_get_int();
- int col = packet_get_int();
- int xpixel = packet_get_int();
- int ypixel = packet_get_int();
+ u_int row = packet_get_int();
+ u_int col = packet_get_int();
+ u_int xpixel = packet_get_int();
+ u_int ypixel = packet_get_int();
debug("Window change received.");
packet_check_eom();
return c;
}
+static Channel *
+server_request_tun(void)
+{
+ Channel *c = NULL;
+ int mode, tun;
+ int sock;
+
+ mode = packet_get_int();
+ switch (mode) {
+ case SSH_TUNMODE_POINTOPOINT:
+ case SSH_TUNMODE_ETHERNET:
+ break;
+ default:
+ packet_send_debug("Unsupported tunnel device mode.");
+ return NULL;
+ }
+ if ((options.permit_tun & mode) == 0) {
+ packet_send_debug("Server has rejected tunnel device "
+ "forwarding");
+ return NULL;
+ }
+
+ tun = packet_get_int();
+ if (forced_tun_device != -1) {
+ if (tun != SSH_TUNID_ANY && forced_tun_device != tun)
+ goto done;
+ tun = forced_tun_device;
+ }
+ sock = tun_open(tun, mode);
+ if (sock < 0)
+ goto done;
+ c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
+ CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
+ c->datagram = 1;
+#if defined(SSH_TUN_FILTER)
+ if (mode == SSH_TUNMODE_POINTOPOINT)
+ channel_register_filter(c->self, sys_tun_infilter,
+ sys_tun_outfilter);
+#endif
+
+ done:
+ if (c == NULL)
+ packet_send_debug("Failed to open the tunnel device.");
+ return c;
+}
+
static Channel *
server_request_session(void)
{
channel_free(c);
return NULL;
}
- channel_register_cleanup(c->self, session_close_by_channel);
+ channel_register_cleanup(c->self, session_close_by_channel, 0);
return c;
}
c = server_request_session();
} else if (strcmp(ctype, "direct-tcpip") == 0) {
c = server_request_direct_tcpip();
+ } else if (strcmp(ctype, "tun@openssh.com") == 0) {
+ c = server_request_tun();
}
if (c != NULL) {
debug("server_input_channel_open: confirm %s", ctype);
success = channel_cancel_rport_listener(cancel_address,
cancel_port);
+ xfree(cancel_address);
}
if (want_reply) {
packet_start(success ?
}
xfree(rtype);
}
+
static void
server_input_channel_req(int type, u_int32_t seq, void *ctxt)
{