*/
#include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.108 2003/05/11 20:30:24 markus Exp $");
+RCSID("$OpenBSD: serverloop.c,v 1.122 2005/12/06 22:38:27 reyk Exp $");
#include "xmalloc.h"
#include "packet.h"
/* XXX */
extern Kex *xxx_kex;
-static Authctxt *xxx_authctxt;
+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);
errno = save_errno;
}
+static void
+sigterm_handler(int sig)
+{
+ received_sigterm = sig;
+}
+
/*
* Make packets from buffered stderr data, and buffer it for sending
* to the client.
static void
make_packets_from_stderr_data(void)
{
- int len;
+ u_int len;
/* Send buffered stderr data to the client. */
while (buffer_len(&stderr_buffer) > 0 &&
static void
make_packets_from_stdout_data(void)
{
- int len;
+ u_int len;
/* Send buffered stdout data to the client. */
while (buffer_len(&stdout_buffer) > 0 &&
static void
client_alive_check(void)
{
- static int had_channel = 0;
- int id;
-
- id = channel_find_open();
- if (id == -1) {
- if (!had_channel)
- return;
- packet_disconnect("No open channels after timeout!");
- }
- had_channel = 1;
+ 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.");
/*
- * send a bogus channel request with "wantreply",
+ * send a bogus global/channel request with "wantreply",
* we should get back a failure
*/
- channel_request_start(id, "keepalive@openssh.com", 1);
+ if ((channel_id = channel_find_open()) == -1) {
+ packet_start(SSH2_MSG_GLOBAL_REQUEST);
+ packet_put_cstring("keepalive@openssh.com");
+ packet_put_char(1); /* boolean: want reply */
+ } else {
+ channel_request_start(channel_id, "keepalive@openssh.com", 1);
+ }
packet_send();
}
*/
static void
wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
- int *nallocp, u_int max_time_milliseconds)
+ u_int *nallocp, u_int max_time_milliseconds)
{
struct timeval tv, *tvp;
int ret;
connection_closed = 1;
if (compat20)
return;
- fatal_cleanup();
+ cleanup_exit(255);
} else if (len < 0) {
if (errno != EINTR && errno != EAGAIN) {
verbose("Read error from remote host "
"%.100s: %.100s",
get_remote_ipaddr(), strerror(errno));
- fatal_cleanup();
+ cleanup_exit(255);
}
} else {
/* Buffer any received data. */
server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
{
fd_set *readset = NULL, *writeset = NULL;
- int max_fd = 0, nalloc = 0;
+ int max_fd = 0;
+ u_int nalloc = 0;
int wait_status; /* Status returned by wait(). */
pid_t wait_pid; /* pid returned by wait(). */
int waiting_termination = 0; /* Have displayed waiting close message. */
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);
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);
max_fd = MAX(max_fd, notify_pipe[0]);
- xxx_authctxt = authctxt;
-
server_init_dispatch();
for (;;) {
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_channel_failure(int type, u_int32_t seq, void *ctxt)
+server_input_keep_alive(int type, u_int32_t seq, void *ctxt)
{
- debug("Got CHANNEL_FAILURE for keepalive");
+ debug("Got %d/%u for keepalive", type, seq);
/*
* reset timeout, since we got a sane answer from the client.
* even if this was generated by something other than
client_alive_timeouts = 0;
}
-
static void
server_input_stdin_data(int type, u_int32_t seq, void *ctxt)
{
}
static Channel *
-server_request_direct_tcpip(char *ctype)
+server_request_direct_tcpip(void)
{
Channel *c;
int sock;
packet_check_eom();
debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
- originator, originator_port, target, target_port);
+ originator, originator_port, target, target_port);
/* XXX check permission */
sock = channel_connect_to(target, target_port);
xfree(originator);
if (sock < 0)
return NULL;
- c = channel_new(ctype, SSH_CHANNEL_CONNECTING,
+ c = channel_new("direct-tcpip", SSH_CHANNEL_CONNECTING,
sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT,
CHAN_TCP_PACKET_DEFAULT, 0, "direct-tcpip", 1);
return c;
}
static Channel *
-server_request_session(char *ctype)
+server_request_tun(void)
+{
+ Channel *c = NULL;
+ int sock, tun;
+
+ if (!options.permit_tun) {
+ packet_send_debug("Server has disabled tunnel device forwarding.");
+ return NULL;
+ }
+
+ tun = packet_get_int();
+ if (forced_tun_device != -1) {
+ if (tun != -1 && forced_tun_device != tun)
+ goto done;
+ tun = forced_tun_device;
+ }
+ sock = tun_open(tun);
+ 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;
+
+ done:
+ if (c == NULL)
+ packet_send_debug("Failed to open the tunnel device.");
+ return c;
+}
+
+static Channel *
+server_request_session(void)
{
Channel *c;
* SSH_CHANNEL_LARVAL. Additionally, a callback for handling all
* CHANNEL_REQUEST messages is registered.
*/
- c = channel_new(ctype, SSH_CHANNEL_LARVAL,
+ c = channel_new("session", SSH_CHANNEL_LARVAL,
-1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
0, "server-session", 1);
- if (session_open(xxx_authctxt, c->self) != 1) {
+ if (session_open(the_authctxt, c->self) != 1) {
debug("session open failed, free channel %d", c->self);
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;
}
ctype, rchan, rwindow, rmaxpack);
if (strcmp(ctype, "session") == 0) {
- c = server_request_session(ctype);
+ c = server_request_session();
} else if (strcmp(ctype, "direct-tcpip") == 0) {
- c = server_request_direct_tcpip(ctype);
+ 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);
char *listen_address;
u_short listen_port;
- pw = auth_get_user();
- if (pw == NULL)
- fatal("server_input_global_request: no user");
- listen_address = packet_get_string(NULL); /* XXX currently ignored */
+ pw = the_authctxt->pw;
+ if (pw == NULL || !the_authctxt->valid)
+ fatal("server_input_global_request: no/invalid user");
+ listen_address = packet_get_string(NULL);
listen_port = (u_short)packet_get_int();
debug("server_input_global_request: tcpip-forward listen %s port %d",
listen_address, listen_port);
#ifndef NO_IPPORT_RESERVED_CONCEPT
|| (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)
#endif
- ) {
+ ) {
success = 0;
packet_send_debug("Server has disabled port forwarding.");
} else {
listen_address, listen_port, options.gateway_ports);
}
xfree(listen_address);
+ } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
+ char *cancel_address;
+ u_short cancel_port;
+
+ cancel_address = packet_get_string(NULL);
+ cancel_port = (u_short)packet_get_int();
+ debug("%s: cancel-tcpip-forward addr %s port %d", __func__,
+ cancel_address, cancel_port);
+
+ success = channel_cancel_rport_listener(cancel_address,
+ cancel_port);
}
if (want_reply) {
packet_start(success ?
dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request);
/* client_alive */
- dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure);
+ dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive);
+ dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive);
+ dispatch_set(SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive);
/* rekeying */
dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
}