*/
#include "includes.h"
+RCSID("$OpenBSD: serverloop.c,v 1.35 2000/11/06 23:04:56 markus Exp $");
+
#include "xmalloc.h"
#include "ssh.h"
#include "packet.h"
#include "session.h"
#include "dispatch.h"
#include "auth-options.h"
+#include "auth.h"
extern ServerOptions options;
/*
* This SIGCHLD kludge is used to detect when the child exits. The server
* will exit after that, as soon as forwarded connections have terminated.
- *
- * After SIGCHLD child_has_selected is set to 1 after the first pass
- * through the wait_until_can_do_something() select(). This ensures
- * that the child's output gets a chance to drain before it is yanked.
*/
static pid_t child_pid; /* Pid of the child. */
static volatile int child_terminated; /* The child has terminated. */
-static volatile int child_has_selected; /* Child has had chance to drain. */
static volatile int child_wait_status; /* Status from wait(). */
void server_init_dispatch(void);
error("Strange, got SIGCHLD and wait returned pid %d but child is %d",
wait_pid, child_pid);
if (WIFEXITED(child_wait_status) ||
- WIFSIGNALED(child_wait_status)) {
+ WIFSIGNALED(child_wait_status))
child_terminated = 1;
- child_has_selected = 0;
- }
}
signal(SIGCHLD, sigchld_handler);
errno = save_errno;
int save_errno = errno;
debug("Received SIGCHLD.");
child_terminated = 1;
- child_has_selected = 0;
+ signal(SIGCHLD, sigchld_handler2);
errno = save_errno;
}
else
goto retry_select;
}
-
- if (child_terminated)
- child_has_selected = 1;
}
/*
/* Initialize the SIGCHLD kludge. */
child_pid = pid;
child_terminated = 0;
- child_has_selected = 0;
signal(SIGCHLD, sigchld_handler);
signal(SIGPIPE, SIG_IGN);
* descriptors, and we have no more data to send to the
* client, and there is no pending buffered data.
*/
- if (((fdout_eof && fderr_eof) ||
- (child_terminated && child_has_selected)) &&
- !packet_have_data_to_write() &&
- (buffer_len(&stdout_buffer) == 0) &&
- (buffer_len(&stderr_buffer) == 0)) {
+ if (fdout_eof && fderr_eof && !packet_have_data_to_write() &&
+ buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) {
if (!channel_still_open())
break;
if (!waiting_termination) {
if (packet_not_very_much_data_to_write())
channel_output_poll();
wait_until_can_do_something(&readset, &writeset, 0);
- if (child_terminated && child_has_selected) {
- /* XXX: race - assumes only one child has terminated */
+ if (child_terminated) {
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
session_close_by_pid(pid, status);
child_terminated = 0;
- child_has_selected = 0;
- signal(SIGCHLD, sigchld_handler2);
}
channel_after_select(&readset, &writeset);
process_input(&readset);
pty_change_window_size(fdin, row, col, xpixel, ypixel);
}
-int
-input_direct_tcpip(void)
+Channel *
+server_request_direct_tcpip(char *ctype)
{
- int sock;
+ int sock, newch;
char *target, *originator;
int target_port, originator_port;
originator_port = packet_get_int();
packet_done();
- debug("open direct-tcpip: from %s port %d to %s port %d",
+ debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
originator, originator_port, target, target_port);
/* XXX check permission */
if (no_port_forwarding_flag || !options.allow_tcp_forwarding) {
xfree(target);
xfree(originator);
- return -1;
+ return NULL;
}
sock = channel_connect_to(target, target_port);
xfree(target);
xfree(originator);
if (sock < 0)
- return -1;
- return channel_new("direct-tcpip", SSH_CHANNEL_OPEN,
+ return NULL;
+ newch = channel_new(ctype, SSH_CHANNEL_OPEN,
sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT,
- CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"));
+ CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1);
+ return (newch >= 0) ? channel_lookup(newch) : NULL;
+}
+
+Channel *
+server_request_session(char *ctype)
+{
+ int newch;
+
+ debug("input_session_request");
+ packet_done();
+ /*
+ * A server session has no fd to read or write until a
+ * CHANNEL_REQUEST for a shell is made, so we set the type to
+ * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all
+ * CHANNEL_REQUEST messages is registered.
+ */
+ newch = channel_new(ctype, SSH_CHANNEL_LARVAL,
+ -1, -1, -1, 0, CHAN_SES_PACKET_DEFAULT,
+ 0, xstrdup("server-session"), 1);
+ if (session_open(newch) == 1) {
+ channel_register_callback(newch, SSH2_MSG_CHANNEL_REQUEST,
+ session_input_channel_req, (void *)0);
+ channel_register_cleanup(newch, session_close_by_channel);
+ return channel_lookup(newch);
+ } else {
+ debug("session open failed, free channel %d", newch);
+ channel_free(newch);
+ }
+ return NULL;
}
void
{
Channel *c = NULL;
char *ctype;
- int id;
unsigned int len;
int rchan;
int rmaxpack;
ctype, rchan, rwindow, rmaxpack);
if (strcmp(ctype, "session") == 0) {
- debug("open session");
- packet_done();
- /*
- * A server session has no fd to read or write
- * until a CHANNEL_REQUEST for a shell is made,
- * so we set the type to SSH_CHANNEL_LARVAL.
- * Additionally, a callback for handling all
- * CHANNEL_REQUEST messages is registered.
- */
- id = channel_new(ctype, SSH_CHANNEL_LARVAL,
- -1, -1, -1, 0, CHAN_SES_PACKET_DEFAULT,
- 0, xstrdup("server-session"));
- if (session_open(id) == 1) {
- channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST,
- session_input_channel_req, (void *)0);
- channel_register_cleanup(id, session_close_by_channel);
- c = channel_lookup(id);
- } else {
- debug("session open failed, free channel %d", id);
- channel_free(id);
- }
+ c = server_request_session(ctype);
} else if (strcmp(ctype, "direct-tcpip") == 0) {
- id = input_direct_tcpip();
- if (id >= 0)
- c = channel_lookup(id);
+ c = server_request_direct_tcpip(ctype);
}
if (c != NULL) {
- debug("confirm %s", ctype);
+ debug("server_input_channel_open: confirm %s", ctype);
c->remote_id = rchan;
c->remote_window = rwindow;
c->remote_maxpacket = rmaxpack;
packet_put_int(c->local_maxpacket);
packet_send();
} else {
- debug("failure %s", ctype);
+ debug("server_input_channel_open: failure %s", ctype);
packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
packet_put_int(rchan);
packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
xfree(ctype);
}
+void
+server_input_global_request(int type, int plen, void *ctxt)
+{
+ char *rtype;
+ int want_reply;
+ int success = 0;
+
+ rtype = packet_get_string(NULL);
+ want_reply = packet_get_char();
+ debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply);
+
+ if (strcmp(rtype, "tcpip-forward") == 0) {
+ struct passwd *pw;
+ 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 */
+ listen_port = (u_short)packet_get_int();
+ debug("server_input_global_request: tcpip-forward listen %s port %d",
+ listen_address, listen_port);
+
+ /* check permissions */
+ if (!options.allow_tcp_forwarding ||
+ no_port_forwarding_flag ||
+ (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)) {
+ success = 0;
+ packet_send_debug("Server has disabled port forwarding.");
+ } else {
+ /* Start listening on the port */
+ channel_request_forwarding(
+ listen_address, listen_port,
+ /*unspec host_to_connect*/ "<unspec host>",
+ /*unspec port_to_connect*/ 0,
+ options.gateway_ports, /*remote*/ 1);
+ success = 1;
+ }
+ xfree(listen_address);
+ }
+ if (want_reply) {
+ packet_start(success ?
+ SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
+ packet_send();
+ packet_write_wait();
+ }
+ xfree(rtype);
+}
+
void
server_init_dispatch_20()
{
dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request);
dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
+ dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request);
}
void
server_init_dispatch_13()