*/
#include "includes.h"
-RCSID("$OpenBSD: buffer.c,v 1.13 2001/04/12 19:15:24 markus Exp $");
+RCSID("$OpenBSD: buffer.c,v 1.23 2005/03/14 11:46:56 markus Exp $");
#include "xmalloc.h"
#include "buffer.h"
void
buffer_init(Buffer *buffer)
{
- buffer->alloc = 4096;
- buffer->buf = xmalloc(buffer->alloc);
+ const u_int len = 4096;
+
+ buffer->alloc = 0;
+ buffer->buf = xmalloc(len);
+ buffer->alloc = len;
buffer->offset = 0;
buffer->end = 0;
}
void
buffer_free(Buffer *buffer)
{
- memset(buffer->buf, 0, buffer->alloc);
- xfree(buffer->buf);
+ if (buffer->alloc > 0) {
+ memset(buffer->buf, 0, buffer->alloc);
+ buffer->alloc = 0;
+ xfree(buffer->buf);
+ }
}
/*
/* Appends data to the buffer, expanding it if necessary. */
void
-buffer_append(Buffer *buffer, const char *data, u_int len)
+buffer_append(Buffer *buffer, const void *data, u_int len)
{
- char *cp;
- buffer_append_space(buffer, &cp, len);
- memcpy(cp, data, len);
+ void *p;
+ p = buffer_append_space(buffer, len);
+ memcpy(p, data, len);
}
/*
* to the allocated region.
*/
-void
-buffer_append_space(Buffer *buffer, char **datap, u_int len)
+void *
+buffer_append_space(Buffer *buffer, u_int len)
{
+ u_int newlen;
+ void *p;
+
+ if (len > BUFFER_MAX_CHUNK)
+ fatal("buffer_append_space: len %u not supported", len);
+
/* If the buffer is empty, start using it from the beginning. */
if (buffer->offset == buffer->end) {
buffer->offset = 0;
restart:
/* If there is enough space to store all data, store it now. */
if (buffer->end + len < buffer->alloc) {
- *datap = buffer->buf + buffer->end;
+ p = buffer->buf + buffer->end;
buffer->end += len;
- return;
+ return p;
}
/*
* If the buffer is quite empty, but all data is at the end, move the
* data to the beginning and retry.
*/
- if (buffer->offset > buffer->alloc / 2) {
+ if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) {
memmove(buffer->buf, buffer->buf + buffer->offset,
buffer->end - buffer->offset);
buffer->end -= buffer->offset;
goto restart;
}
/* Increase the size of the buffer and retry. */
- buffer->alloc += len + 32768;
- buffer->buf = xrealloc(buffer->buf, buffer->alloc);
+
+ newlen = buffer->alloc + len + 32768;
+ if (newlen > BUFFER_MAX_HPN_LEN)
+ fatal("buffer_append_space: alloc %u not supported",
+ newlen);
+ buffer->buf = xrealloc(buffer->buf, newlen);
+ buffer->alloc = newlen;
goto restart;
+ /* NOTREACHED */
}
/* Returns the number of bytes of data in the buffer. */
/* Gets data from the beginning of the buffer. */
-void
-buffer_get(Buffer *buffer, char *buf, u_int len)
+int
+buffer_get_ret(Buffer *buffer, void *buf, u_int len)
{
- if (len > buffer->end - buffer->offset)
- fatal("buffer_get: trying to get more bytes %d than in buffer %d",
+ if (len > buffer->end - buffer->offset) {
+ error("buffer_get_ret: trying to get more bytes %d than in buffer %d",
len, buffer->end - buffer->offset);
+ return (-1);
+ }
memcpy(buf, buffer->buf + buffer->offset, len);
buffer->offset += len;
+ return (0);
+}
+
+void
+buffer_get(Buffer *buffer, void *buf, u_int len)
+{
+ if (buffer_get_ret(buffer, buf, len) == -1)
+ fatal("buffer_get: buffer error");
}
/* Consumes the given number of bytes from the beginning of the buffer. */
+int
+buffer_consume_ret(Buffer *buffer, u_int bytes)
+{
+ if (bytes > buffer->end - buffer->offset) {
+ error("buffer_consume_ret: trying to get more bytes than in buffer");
+ return (-1);
+ }
+ buffer->offset += bytes;
+ return (0);
+}
+
void
buffer_consume(Buffer *buffer, u_int bytes)
{
- if (bytes > buffer->end - buffer->offset)
- fatal("buffer_consume: trying to get more bytes than in buffer");
- buffer->offset += bytes;
+ if (buffer_consume_ret(buffer, bytes) == -1)
+ fatal("buffer_consume: buffer error");
}
/* Consumes the given number of bytes from the end of the buffer. */
+int
+buffer_consume_end_ret(Buffer *buffer, u_int bytes)
+{
+ if (bytes > buffer->end - buffer->offset)
+ return (-1);
+ buffer->end -= bytes;
+ return (0);
+}
+
void
buffer_consume_end(Buffer *buffer, u_int bytes)
{
- if (bytes > buffer->end - buffer->offset)
+ if (buffer_consume_end_ret(buffer, bytes) == -1)
fatal("buffer_consume_end: trying to get more bytes than in buffer");
- buffer->end -= bytes;
}
/* Returns a pointer to the first used byte in the buffer. */
-char *
+void *
buffer_ptr(Buffer *buffer)
{
return buffer->buf + buffer->offset;
void
buffer_dump(Buffer *buffer)
{
- int i;
- u_char *ucp = (u_char *) buffer->buf;
+ u_int i;
+ u_char *ucp = buffer->buf;
for (i = buffer->offset; i < buffer->end; i++) {
fprintf(stderr, "%02x", ucp[i]);
+/* $OpenBSD: buffer.h,v 1.13 2005/03/14 11:46:56 markus Exp $ */
+
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* called by a name other than "ssh" or "Secure Shell".
*/
-/* RCSID("$OpenBSD: buffer.h,v 1.9 2001/06/26 17:27:23 markus Exp $"); */
-
#ifndef BUFFER_H
#define BUFFER_H
typedef struct {
- char *buf; /* Buffer for data. */
+ u_char *buf; /* Buffer for data. */
u_int alloc; /* Number of bytes allocated for data. */
u_int offset; /* Offset of first byte containing data. */
u_int end; /* Offset of last byte containing data. */
} Buffer;
+#define BUFFER_MAX_CHUNK 0x100000
+#define BUFFER_MAX_LEN 0xa00000
+#define BUFFER_MAX_HPN_LEN (2<<29)-1
+
void buffer_init(Buffer *);
void buffer_clear(Buffer *);
void buffer_free(Buffer *);
u_int buffer_len(Buffer *);
-char *buffer_ptr(Buffer *);
+void *buffer_ptr(Buffer *);
-void buffer_append(Buffer *, const char *, u_int);
-void buffer_append_space(Buffer *, char **, u_int);
+void buffer_append(Buffer *, const void *, u_int);
+void *buffer_append_space(Buffer *, u_int);
-void buffer_get(Buffer *, char *, u_int);
+void buffer_get(Buffer *, void *, u_int);
void buffer_consume(Buffer *, u_int);
void buffer_consume_end(Buffer *, u_int);
void buffer_dump(Buffer *);
+int buffer_get_ret(Buffer *, void *, u_int);
+int buffer_consume_ret(Buffer *, u_int);
+int buffer_consume_end_ret(Buffer *, u_int);
+
#endif /* BUFFER_H */
* called by a name other than "ssh" or "Secure Shell".
*
* SSH2 support added by Markus Friedl.
- * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
* Copyright (c) 1999 Dug Song. All rights reserved.
* Copyright (c) 1999 Theo de Raadt. All rights reserved.
*
*/
#include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.140 2001/10/10 22:18:47 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.223 2005/07/17 07:17:54 djm Exp $");
#include "ssh.h"
#include "ssh1.h"
#include "ssh2.h"
#include "packet.h"
#include "xmalloc.h"
-#include "uidswap.h"
#include "log.h"
#include "misc.h"
#include "channels.h"
#include "canohost.h"
#include "key.h"
#include "authfd.h"
-
+#include "pathnames.h"
+#include "bufaux.h"
/* -- channel core */
+#define CHAN_RBUF 16*1024
+
/*
* Pointer to an array containing all allocated channels. The array is
* dynamically extended as needed.
* Size of the channel array. All slots of the array must always be
* initialized (at least the type field); unused slots set to NULL
*/
-static int channels_alloc = 0;
+static u_int channels_alloc = 0;
/*
* Maximum file descriptor value used in any of the channels. This is
/* Maximum number of fake X11 displays to try. */
#define MAX_DISPLAYS 1000
+/* Saved X11 local (client) display. */
+static char *x11_saved_display = NULL;
+
/* Saved X11 authentication protocol name. */
static char *x11_saved_proto = NULL;
#define NUM_SOCKS 10
-/* Name and directory of socket for authentication agent forwarding. */
-static char *auth_sock_name = NULL;
-static char *auth_sock_dir = NULL;
-
/* AF_UNSPEC or AF_INET or AF_INET6 */
static int IPv4or6 = AF_UNSPEC;
{
Channel *c;
- if (id < 0 || id > channels_alloc) {
- log("channel_lookup: %d: bad id", id);
+ if (id < 0 || (u_int)id >= channels_alloc) {
+ logit("channel_lookup: %d: bad id", id);
return NULL;
}
c = channels[id];
if (c == NULL) {
- log("channel_lookup: %d: bad id: channel free", id);
+ logit("channel_lookup: %d: bad id: channel free", id);
return NULL;
}
return c;
c->rfd = rfd;
c->wfd = wfd;
c->sock = (rfd == wfd) ? rfd : -1;
+ c->ctl_fd = -1; /* XXX: set elsewhere */
c->efd = efd;
c->extended_usage = extusage;
/* XXX ugly hack: nonblock is only set by the server */
if (nonblock && isatty(c->rfd)) {
- debug("channel %d: rfd %d isatty", c->self, c->rfd);
+ debug2("channel %d: rfd %d isatty", c->self, c->rfd);
c->isatty = 1;
if (!isatty(c->wfd)) {
error("channel %d: wfd %d is not a tty?",
} else {
c->isatty = 0;
}
+ c->wfd_isatty = isatty(c->wfd);
/* enable nonblocking mode */
if (nonblock) {
Channel *
channel_new(char *ctype, int type, int rfd, int wfd, int efd,
- int window, int maxpack, int extusage, char *remote_name, int nonblock)
+ u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
{
- int i, found;
+ int found;
+ u_int i;
Channel *c;
/* Do initial allocation if this is the first call. */
if (channels_alloc == 0) {
- chan_init();
channels_alloc = 10;
channels = xmalloc(channels_alloc * sizeof(Channel *));
for (i = 0; i < channels_alloc; i++)
channels[i] = NULL;
- fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL);
}
/* Try to find a free slot where to put the new channel. */
for (found = -1, i = 0; i < channels_alloc; i++)
if (channels[i] == NULL) {
/* Found a free slot. */
- found = i;
+ found = (int)i;
break;
}
- if (found == -1) {
+ if (found < 0) {
/* There are no free slots. Take last+1 slot and expand the array. */
found = channels_alloc;
+ if (channels_alloc > 10000)
+ fatal("channel_new: internal error: channels_alloc %d "
+ "too big.", channels_alloc);
+ channels = xrealloc(channels,
+ (channels_alloc + 10) * sizeof(Channel *));
channels_alloc += 10;
debug2("channel: expanding %d", channels_alloc);
- channels = xrealloc(channels, channels_alloc * sizeof(Channel *));
for (i = found; i < channels_alloc; i++)
channels[i] = NULL;
}
buffer_init(&c->input);
buffer_init(&c->output);
buffer_init(&c->extended);
- chan_init_iostates(c);
+ c->ostate = CHAN_OUTPUT_OPEN;
+ c->istate = CHAN_INPUT_OPEN;
+ c->flags = 0;
channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
c->self = found;
c->type = type;
c->local_window_max = window;
c->local_consumed = 0;
c->local_maxpacket = maxpack;
+ c->dynamic_window = 0;
c->remote_id = -1;
- c->remote_name = remote_name;
+ c->remote_name = xstrdup(remote_name);
c->remote_window = 0;
c->remote_maxpacket = 0;
- c->cb_fn = NULL;
- c->cb_arg = NULL;
- c->cb_event = 0;
c->force_drain = 0;
+ c->single_connection = 0;
c->detach_user = NULL;
+ c->confirm = NULL;
+ c->confirm_ctx = NULL;
c->input_filter = NULL;
debug("channel %d: new [%s]", found, remote_name);
return c;
static int
channel_find_maxfd(void)
{
- int i, max = 0;
+ u_int i;
+ int max = 0;
Channel *c;
for (i = 0; i < channels_alloc; i++) {
static void
channel_close_fds(Channel *c)
{
- debug3("channel_close_fds: channel %d: r %d w %d e %d",
- c->self, c->rfd, c->wfd, c->efd);
+ debug3("channel %d: close_fds r %d w %d e %d c %d",
+ c->self, c->rfd, c->wfd, c->efd, c->ctl_fd);
channel_close_fd(&c->sock);
+ channel_close_fd(&c->ctl_fd);
channel_close_fd(&c->rfd);
channel_close_fd(&c->wfd);
channel_close_fd(&c->efd);
channel_free(Channel *c)
{
char *s;
- int i, n;
+ u_int i, n;
for (n = 0, i = 0; i < channels_alloc; i++)
if (channels[i])
n++;
- debug("channel_free: channel %d: %s, nchannels %d", c->self,
+ debug("channel %d: free: %s, nchannels %u", c->self,
c->remote_name ? c->remote_name : "???", n);
s = channel_open_message();
- debug3("channel_free: status: %s", s);
+ debug3("channel %d: status: %s", c->self, s);
xfree(s);
if (c->sock != -1)
shutdown(c->sock, SHUT_RDWR);
+ if (c->ctl_fd != -1)
+ shutdown(c->ctl_fd, SHUT_RDWR);
channel_close_fds(c);
buffer_free(&c->input);
buffer_free(&c->output);
void
channel_free_all(void)
{
- int i;
+ u_int i;
for (i = 0; i < channels_alloc; i++)
if (channels[i] != NULL)
*/
void
-channel_close_all()
+channel_close_all(void)
{
- int i;
+ u_int i;
for (i = 0; i < channels_alloc; i++)
if (channels[i] != NULL)
void
channel_stop_listening(void)
{
- int i;
+ u_int i;
Channel *c;
for (i = 0; i < channels_alloc; i++) {
*/
int
-channel_not_very_much_buffered_data()
+channel_not_very_much_buffered_data(void)
{
u_int i;
Channel *c;
#if 0
if (!compat20 &&
buffer_len(&c->input) > packet_get_maxsize()) {
- debug("channel %d: big input buffer %d",
+ debug2("channel %d: big input buffer %d",
c->self, buffer_len(&c->input));
return 0;
}
#endif
if (buffer_len(&c->output) > packet_get_maxsize()) {
- debug("channel %d: big output buffer %d > %d",
+ debug2("channel %d: big output buffer %u > %u",
c->self, buffer_len(&c->output),
packet_get_maxsize());
return 0;
/* Returns true if any channel is still open. */
int
-channel_still_open()
+channel_still_open(void)
{
- int i;
+ u_int i;
Channel *c;
for (i = 0; i < channels_alloc; i++) {
/* Returns the id of an open channel suitable for keepaliving */
int
-channel_find_open()
+channel_find_open(void)
{
- int i;
+ u_int i;
Channel *c;
for (i = 0; i < channels_alloc; i++) {
c = channels[i];
- if (c == NULL)
+ if (c == NULL || c->remote_id < 0)
continue;
switch (c->type) {
case SSH_CHANNEL_CLOSED:
*/
char *
-channel_open_message()
+channel_open_message(void)
{
Buffer buffer;
Channel *c;
char buf[1024], *cp;
- int i;
+ u_int i;
buffer_init(&buffer);
snprintf(buf, sizeof buf, "The following connections are open:\r\n");
case SSH_CHANNEL_X11_OPEN:
case SSH_CHANNEL_INPUT_DRAINING:
case SSH_CHANNEL_OUTPUT_DRAINING:
- snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
+ snprintf(buf, sizeof buf,
+ " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n",
c->self, c->remote_name,
c->type, c->remote_id,
c->istate, buffer_len(&c->input),
c->ostate, buffer_len(&c->output),
- c->rfd, c->wfd);
+ c->rfd, c->wfd, c->ctl_fd);
buffer_append(&buffer, buf, strlen(buf));
continue;
default:
channel_send_open(int id)
{
Channel *c = channel_lookup(id);
+
if (c == NULL) {
- log("channel_send_open: %d: bad id", id);
+ logit("channel_send_open: %d: bad id", id);
return;
}
- debug("send channel open %d", id);
+ debug2("channel %d: send open", id);
packet_start(SSH2_MSG_CHANNEL_OPEN);
packet_put_cstring(c->ctype);
packet_put_int(c->self);
packet_send();
}
-void
-channel_request(int id, char *service, int wantconfirm)
-{
- channel_request_start(id, service, wantconfirm);
- packet_send();
- debug("channel request %d: %s", id, service) ;
-}
void
channel_request_start(int id, char *service, int wantconfirm)
{
Channel *c = channel_lookup(id);
+
if (c == NULL) {
- log("channel_request: %d: bad id", id);
+ logit("channel_request_start: %d: unknown channel id", id);
return;
}
+ debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
packet_start(SSH2_MSG_CHANNEL_REQUEST);
packet_put_int(c->remote_id);
packet_put_cstring(service);
packet_put_char(wantconfirm);
}
void
-channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg)
+channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
{
Channel *c = channel_lookup(id);
+
if (c == NULL) {
- log("channel_register_callback: %d: bad id", id);
+ logit("channel_register_comfirm: %d: bad id", id);
return;
}
- c->cb_event = mtype;
- c->cb_fn = fn;
- c->cb_arg = arg;
+ c->confirm = fn;
+ c->confirm_ctx = ctx;
}
void
channel_register_cleanup(int id, channel_callback_fn *fn)
{
Channel *c = channel_lookup(id);
+
if (c == NULL) {
- log("channel_register_cleanup: %d: bad id", id);
+ logit("channel_register_cleanup: %d: bad id", id);
return;
}
c->detach_user = fn;
channel_cancel_cleanup(int id)
{
Channel *c = channel_lookup(id);
+
if (c == NULL) {
- log("channel_cancel_cleanup: %d: bad id", id);
+ logit("channel_cancel_cleanup: %d: bad id", id);
return;
}
c->detach_user = NULL;
channel_register_filter(int id, channel_filter_fn *fn)
{
Channel *c = channel_lookup(id);
+
if (c == NULL) {
- log("channel_register_filter: %d: bad id", id);
+ logit("channel_register_filter: %d: bad id", id);
return;
}
c->input_filter = fn;
void
channel_set_fds(int id, int rfd, int wfd, int efd,
- int extusage, int nonblock)
+ int extusage, int nonblock, u_int window_max)
{
Channel *c = channel_lookup(id);
+
if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
fatal("channel_activate for non-larval channel %d.", id);
channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
c->type = SSH_CHANNEL_OPEN;
- /* XXX window size? */
- c->local_window = c->local_window_max = c->local_maxpacket * 2;
+ c->local_window = c->local_window_max = window_max;
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
packet_put_int(c->remote_id);
packet_put_int(c->local_window);
}
static void
-channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
{
- /* test whether sockets are 'alive' for read/write */
- if (c->istate == CHAN_INPUT_OPEN)
- if (buffer_len(&c->input) < packet_get_maxsize())
- FD_SET(c->sock, readset);
- if (c->ostate == CHAN_OUTPUT_OPEN ||
- c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
- if (buffer_len(&c->output) > 0) {
- FD_SET(c->sock, writeset);
- } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
- chan_obuf_empty(c);
- }
- }
-}
+ u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
+
+ /* check buffer limits */
+ limit = MIN(limit, (BUFFER_MAX_HPN_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF));
-static void
-channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset)
-{
if (c->istate == CHAN_INPUT_OPEN &&
- c->remote_window > 0 &&
- buffer_len(&c->input) < c->remote_window)
+ limit > 0 &&
+ buffer_len(&c->input) < limit)
FD_SET(c->rfd, readset);
if (c->ostate == CHAN_OUTPUT_OPEN ||
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
if (buffer_len(&c->output) > 0) {
FD_SET(c->wfd, writeset);
} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
- chan_obuf_empty(c);
+ if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
+ debug2("channel %d: obuf_empty delayed efd %d/(%d)",
+ c->self, c->efd, buffer_len(&c->extended));
+ else
+ chan_obuf_empty(c);
}
}
/** XXX check close conditions, too */
- if (c->efd != -1) {
+ if (compat20 && c->efd != -1) {
if (c->extended_usage == CHAN_EXTENDED_WRITE &&
buffer_len(&c->extended) > 0)
FD_SET(c->efd, writeset);
- else if (c->extended_usage == CHAN_EXTENDED_READ &&
+ else if (!(c->flags & CHAN_EOF_SENT) &&
+ c->extended_usage == CHAN_EXTENDED_READ &&
buffer_len(&c->extended) < c->remote_window)
FD_SET(c->efd, readset);
}
+ /* XXX: What about efd? races? */
+ if (compat20 && c->ctl_fd != -1 &&
+ c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN)
+ FD_SET(c->ctl_fd, readset);
}
static void
packet_put_int(c->remote_id);
packet_send();
c->type = SSH_CHANNEL_CLOSED;
- debug("channel %d: closing after input drain.", c->self);
+ debug2("channel %d: closing after input drain.", c->self);
}
}
return 0;
/* Parse the lengths of variable-length fields. */
- ucp = (u_char *) buffer_ptr(b);
+ ucp = buffer_ptr(b);
if (ucp[0] == 0x42) { /* Byte order MSB first. */
proto_len = 256 * ucp[6] + ucp[7];
data_len = 256 * ucp[8] + ucp[9];
proto_len = ucp[6] + 256 * ucp[7];
data_len = ucp[8] + 256 * ucp[9];
} else {
- debug("Initial X11 packet contains bad byte order byte: 0x%x",
- ucp[0]);
+ debug2("Initial X11 packet contains bad byte order byte: 0x%x",
+ ucp[0]);
return -1;
}
/* Check if authentication protocol matches. */
if (proto_len != strlen(x11_saved_proto) ||
memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
- debug("X11 connection uses different authentication protocol.");
+ debug2("X11 connection uses different authentication protocol.");
return -1;
}
/* Check if authentication data matches our fake data. */
if (data_len != x11_fake_data_len ||
memcmp(ucp + 12 + ((proto_len + 3) & ~3),
x11_fake_data, x11_fake_data_len) != 0) {
- debug("X11 auth data does not match fake data.");
+ debug2("X11 auth data does not match fake data.");
return -1;
}
/* Check fake data length */
channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
{
int ret = x11_open_helper(&c->output);
+
if (ret == 1) {
/* Start normal processing for the channel. */
c->type = SSH_CHANNEL_OPEN;
* We have received an X11 connection that has bad
* authentication information.
*/
- log("X11 connection rejected because of wrong authentication.");
+ logit("X11 connection rejected because of wrong authentication.");
buffer_clear(&c->input);
buffer_clear(&c->output);
channel_close_fd(&c->sock);
if (ret == 1) {
c->type = SSH_CHANNEL_OPEN;
+ channel_pre_open(c, readset, writeset);
+ } else if (ret == -1) {
+ logit("X11 connection rejected because of wrong authentication.");
+ debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
+ chan_read_failed(c);
+ buffer_clear(&c->input);
+ chan_ibuf_empty(c);
+ buffer_clear(&c->output);
+ /* for proto v1, the peer will send an IEOF */
if (compat20)
- channel_pre_open_20(c, readset, writeset);
+ chan_write_failed(c);
else
- channel_pre_open_15(c, readset, writeset);
- } else if (ret == -1) {
- debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
- chan_read_failed(c); /** force close? */
- chan_write_failed(c);
- debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
+ c->type = SSH_CHANNEL_OPEN;
+ debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
}
}
static int
channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
{
- u_char *p, *host;
- int len, have, i, found;
- char username[256];
+ char *p, *host;
+ u_int len, have, i, found;
+ char username[256];
struct {
u_int8_t version;
u_int8_t command;
host = inet_ntoa(s4_req.dest_addr);
strlcpy(c->path, host, sizeof(c->path));
c->host_port = ntohs(s4_req.dest_port);
-
- debug("channel %d: dynamic request: socks4 host %s port %u command %u",
+
+ debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
c->self, host, c->host_port, s4_req.command);
if (s4_req.command != 1) {
return 1;
}
+/* try to decode a socks5 header */
+#define SSH_SOCKS5_AUTHDONE 0x1000
+#define SSH_SOCKS5_NOAUTH 0x00
+#define SSH_SOCKS5_IPV4 0x01
+#define SSH_SOCKS5_DOMAIN 0x03
+#define SSH_SOCKS5_IPV6 0x04
+#define SSH_SOCKS5_CONNECT 0x01
+#define SSH_SOCKS5_SUCCESS 0x00
+
+static int
+channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset)
+{
+ struct {
+ u_int8_t version;
+ u_int8_t command;
+ u_int8_t reserved;
+ u_int8_t atyp;
+ } s5_req, s5_rsp;
+ u_int16_t dest_port;
+ u_char *p, dest_addr[255+1];
+ u_int have, i, found, nmethods, addrlen, af;
+
+ debug2("channel %d: decode socks5", c->self);
+ p = buffer_ptr(&c->input);
+ if (p[0] != 0x05)
+ return -1;
+ have = buffer_len(&c->input);
+ if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
+ /* format: ver | nmethods | methods */
+ if (have < 2)
+ return 0;
+ nmethods = p[1];
+ if (have < nmethods + 2)
+ return 0;
+ /* look for method: "NO AUTHENTICATION REQUIRED" */
+ for (found = 0, i = 2 ; i < nmethods + 2; i++) {
+ if (p[i] == SSH_SOCKS5_NOAUTH ) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
+ c->self);
+ return -1;
+ }
+ buffer_consume(&c->input, nmethods + 2);
+ buffer_put_char(&c->output, 0x05); /* version */
+ buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */
+ FD_SET(c->sock, writeset);
+ c->flags |= SSH_SOCKS5_AUTHDONE;
+ debug2("channel %d: socks5 auth done", c->self);
+ return 0; /* need more */
+ }
+ debug2("channel %d: socks5 post auth", c->self);
+ if (have < sizeof(s5_req)+1)
+ return 0; /* need more */
+ memcpy((char *)&s5_req, p, sizeof(s5_req));
+ if (s5_req.version != 0x05 ||
+ s5_req.command != SSH_SOCKS5_CONNECT ||
+ s5_req.reserved != 0x00) {
+ debug2("channel %d: only socks5 connect supported", c->self);
+ return -1;
+ }
+ switch (s5_req.atyp){
+ case SSH_SOCKS5_IPV4:
+ addrlen = 4;
+ af = AF_INET;
+ break;
+ case SSH_SOCKS5_DOMAIN:
+ addrlen = p[sizeof(s5_req)];
+ af = -1;
+ break;
+ case SSH_SOCKS5_IPV6:
+ addrlen = 16;
+ af = AF_INET6;
+ break;
+ default:
+ debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
+ return -1;
+ }
+ if (have < 4 + addrlen + 2)
+ return 0;
+ buffer_consume(&c->input, sizeof(s5_req));
+ if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
+ buffer_consume(&c->input, 1); /* host string length */
+ buffer_get(&c->input, (char *)&dest_addr, addrlen);
+ buffer_get(&c->input, (char *)&dest_port, 2);
+ dest_addr[addrlen] = '\0';
+ if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
+ strlcpy(c->path, (char *)dest_addr, sizeof(c->path));
+ else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL)
+ return -1;
+ c->host_port = ntohs(dest_port);
+
+ debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
+ c->self, c->path, c->host_port, s5_req.command);
+
+ s5_rsp.version = 0x05;
+ s5_rsp.command = SSH_SOCKS5_SUCCESS;
+ s5_rsp.reserved = 0; /* ignored */
+ s5_rsp.atyp = SSH_SOCKS5_IPV4;
+ ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY;
+ dest_port = 0; /* ignored */
+
+ buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp));
+ buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr));
+ buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port));
+ return 1;
+}
+
/* dynamic port forwarding */
static void
channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
{
u_char *p;
- int have, ret;
+ u_int have;
+ int ret;
have = buffer_len(&c->input);
c->delayed = 0;
debug2("channel %d: pre_dynamic: have %d", c->self, have);
/* buffer_dump(&c->input); */
/* check if the fixed size part of the packet is in buffer. */
- if (have < 4) {
+ if (have < 3) {
/* need more */
FD_SET(c->sock, readset);
return;
case 0x04:
ret = channel_decode_socks4(c, readset, writeset);
break;
+ case 0x05:
+ ret = channel_decode_socks5(c, readset, writeset);
+ break;
default:
ret = -1;
break;
debug("X11 connection requested.");
addrlen = sizeof(addr);
newsock = accept(c->sock, &addr, &addrlen);
+ if (c->single_connection) {
+ debug2("single_connection: closing X11 listener.");
+ channel_close_fd(&c->sock);
+ chan_mark_dead(c);
+ }
if (newsock < 0) {
error("accept: %.100s", strerror(errno));
return;
}
+ set_nodelay(newsock);
remote_ipaddr = get_peer_ipaddr(newsock);
remote_port = get_peer_port(newsock);
snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
nc = channel_new("accepted x11 socket",
SSH_CHANNEL_OPENING, newsock, newsock, -1,
- c->local_window_max, c->local_maxpacket,
- 0, xstrdup(buf), 1);
- if (nc == NULL) {
- close(newsock);
- xfree(remote_ipaddr);
- return;
- }
+ c->local_window_max, c->local_maxpacket, 0, buf, 1);
if (compat20) {
packet_start(SSH2_MSG_CHANNEL_OPEN);
packet_put_cstring("x11");
packet_put_int(nc->self);
- packet_put_int(c->local_window_max);
- packet_put_int(c->local_maxpacket);
+ packet_put_int(nc->local_window_max);
+ packet_put_int(nc->local_maxpacket);
/* originator ipaddr and port */
packet_put_cstring(remote_ipaddr);
if (datafellows & SSH_BUG_X11FWD) {
- debug("ssh2 x11 bug compat mode");
+ debug2("ssh2 x11 bug compat mode");
} else {
packet_put_int(remote_port);
}
int direct;
char buf[1024];
char *remote_ipaddr = get_peer_ipaddr(c->sock);
- u_short remote_port = get_peer_port(c->sock);
+ int remote_port = get_peer_port(c->sock);
direct = (strcmp(rtype, "direct-tcpip") == 0);
}
/* originator host and port */
packet_put_cstring(remote_ipaddr);
- packet_put_int(remote_port);
+ packet_put_int((u_int)remote_port);
packet_send();
} else {
packet_start(SSH_MSG_PORT_OPEN);
error("accept: %.100s", strerror(errno));
return;
}
- nc = channel_new(rtype,
- nextstate, newsock, newsock, -1,
- c->local_window_max, c->local_maxpacket,
- 0, xstrdup(rtype), 1);
- if (nc == NULL) {
- error("channel_post_port_listener: no new channel:");
- close(newsock);
- return;
- }
+ set_nodelay(newsock);
+ nc = channel_new(rtype, nextstate, newsock, newsock, -1,
+ c->local_window_max, c->local_maxpacket, 0, rtype, 1);
nc->listening_port = c->listening_port;
nc->host_port = c->host_port;
strlcpy(nc->path, c->path, sizeof(nc->path));
channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
{
Channel *nc;
- char *name;
int newsock;
struct sockaddr addr;
socklen_t addrlen;
error("accept from auth socket: %.100s", strerror(errno));
return;
}
- name = xstrdup("accepted auth socket");
nc = channel_new("accepted auth socket",
SSH_CHANNEL_OPENING, newsock, newsock, -1,
c->local_window_max, c->local_maxpacket,
- 0, name, 1);
- if (nc == NULL) {
- error("channel_post_auth_listener: channel_new failed");
- xfree(name);
- close(newsock);
- }
+ 0, "accepted auth socket", 1);
if (compat20) {
packet_start(SSH2_MSG_CHANNEL_OPEN);
packet_put_cstring("auth-agent@openssh.com");
socklen_t sz = sizeof(err);
if (FD_ISSET(c->sock, writeset)) {
- if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, (char *)&err,
- &sz) < 0) {
+ if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
err = errno;
error("getsockopt SO_ERROR failed");
}
static int
channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
{
- char buf[16*1024];
+ char buf[CHAN_RBUF];
int len;
if (c->rfd != -1 &&
if (len < 0 && (errno == EINTR || errno == EAGAIN))
return 1;
if (len <= 0) {
- debug("channel %d: read<=0 rfd %d len %d",
+ debug2("channel %d: read<=0 rfd %d len %d",
c->self, c->rfd, len);
if (c->type != SSH_CHANNEL_OPEN) {
- debug("channel %d: not open", c->self);
+ debug2("channel %d: not open", c->self);
chan_mark_dead(c);
return -1;
} else if (compat13) {
- buffer_consume(&c->output, buffer_len(&c->output));
+ buffer_clear(&c->output);
c->type = SSH_CHANNEL_INPUT_DRAINING;
- debug("channel %d: input draining.", c->self);
+ debug2("channel %d: input draining.", c->self);
} else {
chan_read_failed(c);
}
return -1;
}
- if(c->input_filter != NULL) {
+ if (c->input_filter != NULL) {
if (c->input_filter(c, buf, len) == -1) {
- debug("channel %d: filter stops", c->self);
+ debug2("channel %d: filter stops", c->self);
chan_read_failed(c);
}
} else {
buffer_len(&c->output) > 0) {
data = buffer_ptr(&c->output);
dlen = buffer_len(&c->output);
+#ifdef _AIX
+ /* XXX: Later AIX versions can't push as much data to tty */
+ if (compat20 && c->wfd_isatty)
+ dlen = MIN(dlen, 8*1024);
+#endif
len = write(c->wfd, data, dlen);
if (len < 0 && (errno == EINTR || errno == EAGAIN))
return 1;
if (len <= 0) {
if (c->type != SSH_CHANNEL_OPEN) {
- debug("channel %d: not open", c->self);
+ debug2("channel %d: not open", c->self);
chan_mark_dead(c);
return -1;
} else if (compat13) {
- buffer_consume(&c->output, buffer_len(&c->output));
- debug("channel %d: input draining.", c->self);
+ buffer_clear(&c->output);
+ debug2("channel %d: input draining.", c->self);
c->type = SSH_CHANNEL_INPUT_DRAINING;
} else {
chan_write_failed(c);
static int
channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
{
- char buf[16*1024];
+ char buf[CHAN_RBUF];
int len;
/** XXX handle drain efd, too */
FD_ISSET(c->efd, readset)) {
len = read(c->efd, buf, sizeof(buf));
debug2("channel %d: read %d from efd %d",
- c->self, len, c->efd);
+ c->self, len, c->efd);
if (len < 0 && (errno == EINTR || errno == EAGAIN))
return 1;
if (len <= 0) {
return 1;
}
static int
+channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset)
+{
+ char buf[16];
+ int len;
+
+ /* Monitor control fd to detect if the slave client exits */
+ if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) {
+ len = read(c->ctl_fd, buf, sizeof(buf));
+ if (len < 0 && (errno == EINTR || errno == EAGAIN))
+ return 1;
+ if (len <= 0) {
+ debug2("channel %d: ctl read<=0", c->self);
+ if (c->type != SSH_CHANNEL_OPEN) {
+ debug2("channel %d: not open", c->self);
+ chan_mark_dead(c);
+ return -1;
+ } else {
+ chan_read_failed(c);
+ chan_write_failed(c);
+ }
+ return -1;
+ } else
+ fatal("%s: unexpected data on ctl fd", __func__);
+ }
+ return 1;
+}
+static int
channel_check_window(Channel *c)
{
if (c->type == SSH_CHANNEL_OPEN &&
!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
c->local_window < c->local_window_max/2 &&
c->local_consumed > 0) {
+ u_int32_t tcpwinsz = 0;
+ socklen_t optsz = sizeof(tcpwinsz);
+ int ret = -1;
+ u_int32_t addition = 0;
+ if (c->dynamic_window) {
+ ret = getsockopt(packet_get_connection_in(),
+ SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz);
+ if ((ret == 0) && tcpwinsz > BUFFER_MAX_HPN_LEN)
+ tcpwinsz = BUFFER_MAX_HPN_LEN;
+ }
+ if (c->dynamic_window && (ret == 0) &&
+ (tcpwinsz > c->local_window_max)) {
+ addition = tcpwinsz - c->local_window_max;
+ c->local_window_max += addition;
+ }
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
packet_put_int(c->remote_id);
- packet_put_int(c->local_consumed);
+ packet_put_int(c->local_consumed + addition);
packet_send();
debug2("channel %d: window %d sent adjust %d",
c->self, c->local_window,
c->local_consumed);
- c->local_window += c->local_consumed;
+ c->local_window += c->local_consumed + addition;
c->local_consumed = 0;
}
return 1;
}
static void
-channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
{
if (c->delayed)
return;
channel_handle_rfd(c, readset, writeset);
channel_handle_wfd(c, readset, writeset);
-}
-
-static void
-channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset)
-{
- if (c->delayed)
+ if (!compat20)
return;
- channel_handle_rfd(c, readset, writeset);
- channel_handle_wfd(c, readset, writeset);
channel_handle_efd(c, readset, writeset);
-
+ channel_handle_ctl(c, readset, writeset);
channel_check_window(c);
}
channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
{
int len;
+
/* Send buffered output data to the socket. */
if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
len = write(c->sock, buffer_ptr(&c->output),
buffer_len(&c->output));
if (len <= 0)
- buffer_consume(&c->output, buffer_len(&c->output));
+ buffer_clear(&c->output);
else
buffer_consume(&c->output, len);
}
static void
channel_handler_init_20(void)
{
- channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20;
+ channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open;
channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open;
channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
- channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2;
+ channel_post[SSH_CHANNEL_OPEN] = &channel_post_open;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
- channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_2;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open;
}
static void
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
- channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1;
+ channel_post[SSH_CHANNEL_OPEN] = &channel_post_open;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
- channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_1;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open;
}
static void
channel_handler_init_15(void)
{
- channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_15;
+ channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open;
channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open;
channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
- channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1;
+ channel_post[SSH_CHANNEL_OPEN] = &channel_post_open;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
- channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_1;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open;
}
static void
channel_handler_init(void)
{
int i;
- for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
+
+ for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
channel_pre[i] = NULL;
channel_post[i] = NULL;
}
if (c->detach_user != NULL) {
if (!chan_is_dead(c, 0))
return;
- debug("channel %d: gc: notify user", c->self);
+ debug2("channel %d: gc: notify user", c->self);
c->detach_user(c->self, NULL);
/* if we still have a callback */
if (c->detach_user != NULL)
return;
- debug("channel %d: gc: user detached", c->self);
+ debug2("channel %d: gc: user detached", c->self);
}
if (!chan_is_dead(c, 1))
return;
- debug("channel %d: garbage collecting", c->self);
+ debug2("channel %d: garbage collecting", c->self);
channel_free(c);
}
channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
{
static int did_init = 0;
- int i;
+ u_int i;
Channel *c;
if (!did_init) {
*/
void
channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
- int *nallocp, int rekeying)
+ u_int *nallocp, int rekeying)
{
- int n;
- u_int sz;
+ u_int n, sz;
n = MAX(*maxfdp, channel_max_fd);
/* If there is data to send to the connection, enqueue some of it now. */
void
-channel_output_poll()
+channel_output_poll(void)
{
- int len, i;
Channel *c;
+ u_int i, len;
for (i = 0; i < channels_alloc; i++) {
c = channels[i];
fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
/*
* input-buffer is empty and read-socket shutdown:
- * tell peer, that we will not send more data: send IEOF
+ * tell peer, that we will not send more data: send IEOF.
+ * hack for extended data: delay EOF if EFD still in use.
*/
- chan_ibuf_empty(c);
+ if (CHANNEL_EFD_INPUT_ACTIVE(c))
+ debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
+ c->self, c->efd, buffer_len(&c->extended));
+ else
+ chan_ibuf_empty(c);
}
/* Send extended data, i.e. stderr */
if (compat20 &&
+ !(c->flags & CHAN_EOF_SENT) &&
c->remote_window > 0 &&
(len = buffer_len(&c->extended)) > 0 &&
c->extended_usage == CHAN_EXTENDED_READ) {
- debug2("channel %d: rwin %d elen %d euse %d",
+ debug2("channel %d: rwin %u elen %u euse %d",
c->self, c->remote_window, buffer_len(&c->extended),
c->extended_usage);
if (len > c->remote_window)
/* -- protocol input */
void
-channel_input_data(int type, int plen, void *ctxt)
+channel_input_data(int type, u_int32_t seq, void *ctxt)
{
int id;
char *data;
c->type != SSH_CHANNEL_X11_OPEN)
return;
- /* same for protocol 1.5 if output end is no longer open */
- if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN)
- return;
-
/* Get the data. */
data = packet_get_string(&data_len);
- packet_done();
- if (compat20){
+ /*
+ * Ignore data for protocol > 1.3 if output end is no longer open.
+ * For protocol 2 the sending side is reducing its window as it sends
+ * data, so we must 'fake' consumption of the data in order to ensure
+ * that window updates are sent back. Otherwise the connection might
+ * deadlock.
+ */
+ if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) {
+ if (compat20) {
+ c->local_window -= data_len;
+ c->local_consumed += data_len;
+ }
+ xfree(data);
+ return;
+ }
+
+ if (compat20) {
if (data_len > c->local_maxpacket) {
- log("channel %d: rcvd big packet %d, maxpack %d",
+ logit("channel %d: rcvd big packet %d, maxpack %d",
c->self, data_len, c->local_maxpacket);
}
if (data_len > c->local_window) {
- log("channel %d: rcvd too much data %d, win %d",
+ logit("channel %d: rcvd too much data %d, win %d",
c->self, data_len, c->local_window);
xfree(data);
return;
}
c->local_window -= data_len;
- }else{
- packet_integrity_check(plen, 4 + 4 + data_len, type);
}
+ packet_check_eom();
buffer_append(&c->output, data, data_len);
xfree(data);
}
void
-channel_input_extended_data(int type, int plen, void *ctxt)
+channel_input_extended_data(int type, u_int32_t seq, void *ctxt)
{
int id;
- int tcode;
char *data;
- u_int data_len;
+ u_int data_len, tcode;
Channel *c;
/* Get the channel number and verify it. */
if (c == NULL)
packet_disconnect("Received extended_data for bad channel %d.", id);
if (c->type != SSH_CHANNEL_OPEN) {
- log("channel %d: ext data for non open", id);
+ logit("channel %d: ext data for non open", id);
return;
}
+ if (c->flags & CHAN_EOF_RCVD) {
+ if (datafellows & SSH_BUG_EXTEOF)
+ debug("channel %d: accepting ext data after eof", id);
+ else
+ packet_disconnect("Received extended_data after EOF "
+ "on channel %d.", id);
+ }
tcode = packet_get_int();
if (c->efd == -1 ||
c->extended_usage != CHAN_EXTENDED_WRITE ||
tcode != SSH2_EXTENDED_DATA_STDERR) {
- log("channel %d: bad ext data", c->self);
+ logit("channel %d: bad ext data", c->self);
return;
}
data = packet_get_string(&data_len);
- packet_done();
+ packet_check_eom();
if (data_len > c->local_window) {
- log("channel %d: rcvd too much extended_data %d, win %d",
+ logit("channel %d: rcvd too much extended_data %d, win %d",
c->self, data_len, c->local_window);
xfree(data);
return;
}
void
-channel_input_ieof(int type, int plen, void *ctxt)
+channel_input_ieof(int type, u_int32_t seq, void *ctxt)
{
int id;
Channel *c;
- packet_integrity_check(plen, 4, type);
-
id = packet_get_int();
+ packet_check_eom();
c = channel_lookup(id);
if (c == NULL)
packet_disconnect("Received ieof for nonexistent channel %d.", id);
chan_rcvd_ieof(c);
/* XXX force input close */
- if (c->force_drain) {
+ if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
debug("channel %d: FORCE input drain", c->self);
c->istate = CHAN_INPUT_WAIT_DRAIN;
+ if (buffer_len(&c->input) == 0)
+ chan_ibuf_empty(c);
}
}
void
-channel_input_close(int type, int plen, void *ctxt)
+channel_input_close(int type, u_int32_t seq, void *ctxt)
{
int id;
Channel *c;
- packet_integrity_check(plen, 4, type);
-
id = packet_get_int();
+ packet_check_eom();
c = channel_lookup(id);
if (c == NULL)
packet_disconnect("Received close for nonexistent channel %d.", id);
* Not a closed channel - mark it as draining, which will
* cause it to be freed later.
*/
- buffer_consume(&c->input, buffer_len(&c->input));
+ buffer_clear(&c->input);
c->type = SSH_CHANNEL_OUTPUT_DRAINING;
}
}
/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
void
-channel_input_oclose(int type, int plen, void *ctxt)
+channel_input_oclose(int type, u_int32_t seq, void *ctxt)
{
int id = packet_get_int();
Channel *c = channel_lookup(id);
- packet_integrity_check(plen, 4, type);
+
+ packet_check_eom();
if (c == NULL)
packet_disconnect("Received oclose for nonexistent channel %d.", id);
chan_rcvd_oclose(c);
}
void
-channel_input_close_confirmation(int type, int plen, void *ctxt)
+channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
{
int id = packet_get_int();
Channel *c = channel_lookup(id);
- packet_done();
+ packet_check_eom();
if (c == NULL)
packet_disconnect("Received close confirmation for "
"out-of-range channel %d.", id);
}
void
-channel_input_open_confirmation(int type, int plen, void *ctxt)
+channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
{
int id, remote_id;
Channel *c;
- if (!compat20)
- packet_integrity_check(plen, 4 + 4, type);
-
id = packet_get_int();
c = channel_lookup(id);
if (compat20) {
c->remote_window = packet_get_int();
c->remote_maxpacket = packet_get_int();
- packet_done();
- if (c->cb_fn != NULL && c->cb_event == type) {
+ if (c->confirm) {
debug2("callback start");
- c->cb_fn(c->self, c->cb_arg);
+ c->confirm(c->self, c->confirm_ctx);
debug2("callback done");
}
- debug("channel %d: open confirm rwindow %d rmax %d", c->self,
+ debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
c->remote_window, c->remote_maxpacket);
}
+ packet_check_eom();
}
static char *
reason2txt(int reason)
{
- switch(reason) {
+ switch (reason) {
case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
return "administratively prohibited";
case SSH2_OPEN_CONNECT_FAILED:
}
void
-channel_input_open_failure(int type, int plen, void *ctxt)
+channel_input_open_failure(int type, u_int32_t seq, void *ctxt)
{
int id, reason;
char *msg = NULL, *lang = NULL;
Channel *c;
- if (!compat20)
- packet_integrity_check(plen, 4, type);
-
id = packet_get_int();
c = channel_lookup(id);
msg = packet_get_string(NULL);
lang = packet_get_string(NULL);
}
- packet_done();
- log("channel %d: open failed: %s%s%s", id,
+ logit("channel %d: open failed: %s%s%s", id,
reason2txt(reason), msg ? ": ": "", msg ? msg : "");
if (msg != NULL)
xfree(msg);
if (lang != NULL)
xfree(lang);
}
+ packet_check_eom();
/* Free the channel. This will also close the socket. */
channel_free(c);
}
void
-channel_input_channel_request(int type, int plen, void *ctxt)
+channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
{
- int id;
Channel *c;
-
- id = packet_get_int();
- c = channel_lookup(id);
-
- if (c == NULL ||
- (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL))
- packet_disconnect("Received request for "
- "non-open channel %d.", id);
- if (c->cb_fn != NULL && c->cb_event == type) {
- debug2("callback start");
- c->cb_fn(c->self, c->cb_arg);
- debug2("callback done");
- } else {
- char *service = packet_get_string(NULL);
- debug("channel %d: rcvd request for %s", c->self, service);
- debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event);
- xfree(service);
- }
-}
-
-void
-channel_input_window_adjust(int type, int plen, void *ctxt)
-{
- Channel *c;
- int id, adjust;
+ int id;
+ u_int adjust;
if (!compat20)
return;
c = channel_lookup(id);
if (c == NULL || c->type != SSH_CHANNEL_OPEN) {
- log("Received window adjust for "
+ logit("Received window adjust for "
"non-open channel %d.", id);
return;
}
adjust = packet_get_int();
- packet_done();
- debug2("channel %d: rcvd adjust %d", id, adjust);
+ packet_check_eom();
+ debug2("channel %d: rcvd adjust %u", id, adjust);
c->remote_window += adjust;
}
void
-channel_input_port_open(int type, int plen, void *ctxt)
+channel_input_port_open(int type, u_int32_t seq, void *ctxt)
{
Channel *c = NULL;
u_short host_port;
} else {
originator_string = xstrdup("unknown (remote did not supply name)");
}
- packet_done();
+ packet_check_eom();
sock = channel_connect_to(host, host_port);
if (sock != -1) {
c = channel_new("connected socket",
SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
originator_string, 1);
- if (c == NULL) {
- error("channel_input_port_open: channel_new failed");
- close(sock);
- } else {
- c->remote_id = remote_id;
- }
+ c->remote_id = remote_id;
}
+ xfree(originator_string);
if (c == NULL) {
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
packet_put_int(remote_id);
IPv4or6 = af;
}
-/*
- * Initiate forwarding of connections to local port "port" through the secure
- * channel to host:port from remote side.
- */
-int
-channel_request_local_forwarding(u_short listen_port, const char *host_to_connect,
- u_short port_to_connect, int gateway_ports)
-{
- return channel_request_forwarding(
- NULL, listen_port,
- host_to_connect, port_to_connect,
- gateway_ports, /*remote_fwd*/ 0);
-}
-
-/*
- * If 'remote_fwd' is true we have a '-R style' listener for protocol 2
- * (SSH_CHANNEL_RPORT_LISTENER).
- */
-int
-channel_request_forwarding(
- const char *listen_address, u_short listen_port,
- const char *host_to_connect, u_short port_to_connect,
- int gateway_ports, int remote_fwd)
+static int
+channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
+ const char *host_to_connect, u_short port_to_connect, int gateway_ports)
{
Channel *c;
- int success, sock, on = 1, type;
+ int sock, r, success = 0, on = 1, wildcard = 0, is_client;
struct addrinfo hints, *ai, *aitop;
+ const char *host, *addr;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
- const char *host;
- struct linger linger;
- success = 0;
+ host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
+ listen_addr : host_to_connect;
+ is_client = (type == SSH_CHANNEL_PORT_LISTENER);
- if (remote_fwd) {
- host = listen_address;
- type = SSH_CHANNEL_RPORT_LISTENER;
- } else {
- host = host_to_connect;
- type = SSH_CHANNEL_PORT_LISTENER;
+ if (host == NULL) {
+ error("No forward host name.");
+ return 0;
}
-
if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
error("Forward host name too long.");
- return success;
+ return 0;
}
- /* XXX listen_address is currently ignored */
+ /*
+ * Determine whether or not a port forward listens to loopback,
+ * specified address or wildcard. On the client, a specified bind
+ * address will always override gateway_ports. On the server, a
+ * gateway_ports of 1 (``yes'') will override the client's
+ * specification and force a wildcard bind, whereas a value of 2
+ * (``clientspecified'') will bind to whatever address the client
+ * asked for.
+ *
+ * Special-case listen_addrs are:
+ *
+ * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
+ * "" (empty string), "*" -> wildcard v4/v6
+ * "localhost" -> loopback v4/v6
+ */
+ addr = NULL;
+ if (listen_addr == NULL) {
+ /* No address specified: default to gateway_ports setting */
+ if (gateway_ports)
+ wildcard = 1;
+ } else if (gateway_ports || is_client) {
+ if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
+ strcmp(listen_addr, "0.0.0.0") == 0) ||
+ *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
+ (!is_client && gateway_ports == 1))
+ wildcard = 1;
+ else if (strcmp(listen_addr, "localhost") != 0)
+ addr = listen_addr;
+ }
+
+ debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
+ type, wildcard, (addr == NULL) ? "NULL" : addr);
+
/*
* getaddrinfo returns a loopback address if the hostname is
* set to NULL and hints.ai_flags is not AI_PASSIVE
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = IPv4or6;
- hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
+ hints.ai_flags = wildcard ? AI_PASSIVE : 0;
hints.ai_socktype = SOCK_STREAM;
snprintf(strport, sizeof strport, "%d", listen_port);
- if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
- packet_disconnect("getaddrinfo: fatal error");
+ if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
+ if (addr == NULL) {
+ /* This really shouldn't happen */
+ packet_disconnect("getaddrinfo: fatal error: %s",
+ gai_strerror(r));
+ } else {
+ error("channel_setup_fwd_listener: "
+ "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
+ }
+ return 0;
+ }
for (ai = aitop; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
- error("channel_request_forwarding: getnameinfo failed");
+ error("channel_setup_fwd_listener: getnameinfo failed");
continue;
}
/* Create a port to listen for the host. */
- sock = socket(ai->ai_family, SOCK_STREAM, 0);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
/* this is no error since kernel may not support ipv6 */
verbose("socket: %.100s", strerror(errno));
continue;
}
/*
- * Set socket options. We would like the socket to disappear
- * as soon as it has been closed for whatever reason.
+ * Set socket options.
+ * Allow local port reuse in TIME_WAIT.
*/
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
- linger.l_onoff = 1;
- linger.l_linger = 5;
- setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
+ sizeof(on)) == -1)
+ error("setsockopt SO_REUSEADDR: %s", strerror(errno));
+
debug("Local forwarding listening on %s port %s.", ntop, strport);
/* Bind the socket to the address. */
continue;
}
/* Start listening for connections on the socket. */
- if (listen(sock, 5) < 0) {
+ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
error("listen: %.100s", strerror(errno));
close(sock);
continue;
/* Allocate a channel number for the socket. */
c = channel_new("port listener", type, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
- 0, xstrdup("port listener"), 1);
- if (c == NULL) {
- error("channel_request_forwarding: channel_new failed");
- close(sock);
- continue;
- }
+ 0, "port listener", 1);
strlcpy(c->path, host, sizeof(c->path));
c->host_port = port_to_connect;
c->listening_port = listen_port;
success = 1;
}
if (success == 0)
- error("channel_request_forwarding: cannot listen to port: %d",
+ error("channel_setup_fwd_listener: cannot listen to port: %d",
listen_port);
freeaddrinfo(aitop);
return success;
}
+int
+channel_cancel_rport_listener(const char *host, u_short port)
+{
+ u_int i;
+ int found = 0;
+
+ for (i = 0; i < channels_alloc; i++) {
+ Channel *c = channels[i];
+
+ if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
+ strncmp(c->path, host, sizeof(c->path)) == 0 &&
+ c->listening_port == port) {
+ debug2("%s: close channel %d", __func__, i);
+ channel_free(c);
+ found = 1;
+ }
+ }
+
+ return (found);
+}
+
+/* protocol local port fwd, used by ssh (and sshd in v1) */
+int
+channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
+ const char *host_to_connect, u_short port_to_connect, int gateway_ports)
+{
+ return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
+ listen_host, listen_port, host_to_connect, port_to_connect,
+ gateway_ports);
+}
+
+/* protocol v2 remote port fwd, used by sshd */
+int
+channel_setup_remote_fwd_listener(const char *listen_address,
+ u_short listen_port, int gateway_ports)
+{
+ return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
+ listen_address, listen_port, NULL, 0, gateway_ports);
+}
+
/*
* Initiate forwarding of connections to port "port" on remote host through
* the secure channel to host:port from local side.
*/
void
-channel_request_remote_forwarding(u_short listen_port,
+channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
const char *host_to_connect, u_short port_to_connect)
{
- int payload_len, type, success = 0;
+ int type, success = 0;
/* Record locally that connection to this host/port is permitted. */
if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
/* Send the forward request to the remote side. */
if (compat20) {
- const char *address_to_bind = "0.0.0.0";
+ const char *address_to_bind;
+ if (listen_host == NULL)
+ address_to_bind = "localhost";
+ else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0)
+ address_to_bind = "";
+ else
+ address_to_bind = listen_host;
+
packet_start(SSH2_MSG_GLOBAL_REQUEST);
packet_put_cstring("tcpip-forward");
- packet_put_char(0); /* boolean: want reply */
+ packet_put_char(1); /* boolean: want reply */
packet_put_cstring(address_to_bind);
packet_put_int(listen_port);
packet_send();
packet_write_wait();
/* Wait for response from the remote side. */
- type = packet_read(&payload_len);
+ type = packet_read();
switch (type) {
case SSH_SMSG_SUCCESS:
success = 1;
break;
case SSH_SMSG_FAILURE:
- log("Warning: Server denied remote port forwarding.");
+ logit("Warning: Server denied remote port forwarding.");
break;
default:
/* Unknown packet */
}
}
+/*
+ * Request cancellation of remote forwarding of connection host:port from
+ * local side.
+ */
+void
+channel_request_rforward_cancel(const char *host, u_short port)
+{
+ int i;
+
+ if (!compat20)
+ return;
+
+ for (i = 0; i < num_permitted_opens; i++) {
+ if (permitted_opens[i].host_to_connect != NULL &&
+ permitted_opens[i].listen_port == port)
+ break;
+ }
+ if (i >= num_permitted_opens) {
+ debug("%s: requested forward not found", __func__);
+ return;
+ }
+ packet_start(SSH2_MSG_GLOBAL_REQUEST);
+ packet_put_cstring("cancel-tcpip-forward");
+ packet_put_char(0);
+ packet_put_cstring(host == NULL ? "" : host);
+ packet_put_int(port);
+ packet_send();
+
+ permitted_opens[i].listen_port = 0;
+ permitted_opens[i].port_to_connect = 0;
+ free(permitted_opens[i].host_to_connect);
+ permitted_opens[i].host_to_connect = NULL;
+}
+
/*
* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates
* listening for the port, and sends back a success reply (or disconnect
* privileged port.
*/
if (port < IPPORT_RESERVED && !is_root)
- packet_disconnect("Requested forwarding of port %d but user is not root.",
- port);
+ packet_disconnect(
+ "Requested forwarding of port %d but user is not root.",
+ port);
+ if (host_port == 0)
+ packet_disconnect("Dynamic forwarding denied.");
#endif
+
/* Initiate forwarding */
- channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
+ channel_setup_local_fwd_listener(NULL, port, hostname,
+ host_port, gateway_ports);
/* Free the argument string. */
xfree(hostname);
* anyway, and the server has no way to know but to trust the client anyway.
*/
void
-channel_permit_all_opens()
+channel_permit_all_opens(void)
{
if (num_permitted_opens == 0)
all_opens_permitted = 1;
int i;
for (i = 0; i < num_permitted_opens; i++)
- xfree(permitted_opens[i].host_to_connect);
+ if (permitted_opens[i].host_to_connect != NULL)
+ xfree(permitted_opens[i].host_to_connect);
num_permitted_opens = 0;
}
error("connect_to: getnameinfo failed");
continue;
}
- sock = socket(ai->ai_family, SOCK_STREAM, 0);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
- error("socket: %.100s", strerror(errno));
+ if (ai->ai_next == NULL)
+ error("socket: %.100s", strerror(errno));
+ else
+ verbose("socket: %.100s", strerror(errno));
continue;
}
- if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
- fatal("connect_to: F_SETFL: %s", strerror(errno));
+ if (set_nonblock(sock) == -1)
+ fatal("%s: set_nonblock(%d)", __func__, sock);
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
errno != EINPROGRESS) {
error("connect_to %.100s port %s: %.100s", ntop, strport,
return -1;
}
/* success */
+ set_nodelay(sock);
return sock;
}
int i;
for (i = 0; i < num_permitted_opens; i++)
- if (permitted_opens[i].listen_port == listen_port)
+ if (permitted_opens[i].host_to_connect != NULL &&
+ permitted_opens[i].listen_port == listen_port)
return connect_to(
permitted_opens[i].host_to_connect,
permitted_opens[i].port_to_connect);
permit = all_opens_permitted;
if (!permit) {
for (i = 0; i < num_permitted_opens; i++)
- if (permitted_opens[i].port_to_connect == port &&
+ if (permitted_opens[i].host_to_connect != NULL &&
+ permitted_opens[i].port_to_connect == port &&
strcmp(permitted_opens[i].host_to_connect, host) == 0)
permit = 1;
}
if (!permit) {
- log("Received request to connect to host %.100s port %d, "
+ logit("Received request to connect to host %.100s port %d, "
"but the request was denied.", host, port);
return -1;
}
return connect_to(host, port);
}
+void
+channel_send_window_changes(void)
+{
+ u_int i;
+ struct winsize ws;
+
+ for (i = 0; i < channels_alloc; i++) {
+ if (channels[i] == NULL || !channels[i]->client_tty ||
+ channels[i]->type != SSH_CHANNEL_OPEN)
+ continue;
+ if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
+ continue;
+ channel_request_start(i, "window-change", 0);
+ packet_put_int(ws.ws_col);
+ packet_put_int(ws.ws_row);
+ packet_put_int(ws.ws_xpixel);
+ packet_put_int(ws.ws_ypixel);
+ packet_send();
+ }
+}
+
/* -- X11 forwarding */
/*
* Creates an internet domain socket for listening for X11 connections.
- * Returns a suitable value for the DISPLAY variable, or NULL if an error
- * occurs.
+ * Returns 0 and a suitable display number for the DISPLAY variable
+ * stored in display_numberp , or -1 if an error occurs.
*/
-char *
-x11_create_display_inet(int screen_number, int x11_display_offset)
+int
+x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
+ int single_connection, u_int *display_numberp, int **chanids)
{
+ Channel *nc = NULL;
int display_number, sock;
u_short port;
struct addrinfo hints, *ai, *aitop;
char strport[NI_MAXSERV];
int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
- char display[512];
- char hostname[MAXHOSTNAMELEN];
for (display_number = x11_display_offset;
- display_number < MAX_DISPLAYS;
- display_number++) {
+ display_number < MAX_DISPLAYS;
+ display_number++) {
port = 6000 + display_number;
memset(&hints, 0, sizeof(hints));
hints.ai_family = IPv4or6;
- hints.ai_flags = AI_PASSIVE; /* XXX loopback only ? */
+ hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
snprintf(strport, sizeof strport, "%d", port);
if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
error("getaddrinfo: %.100s", gai_strerror(gaierr));
- return NULL;
+ return -1;
}
for (ai = aitop; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
- sock = socket(ai->ai_family, SOCK_STREAM, 0);
+ sock = socket(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol);
if (sock < 0) {
if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) {
error("socket: %.100s", strerror(errno));
- return NULL;
+ freeaddrinfo(aitop);
+ return -1;
} else {
debug("x11_create_display_inet: Socket family %d not supported",
ai->ai_family);
continue;
}
}
+#ifdef IPV6_V6ONLY
+ if (ai->ai_family == AF_INET6) {
+ int on = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
+ error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
+ }
+#endif
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
- debug("bind port %d: %.100s", port, strerror(errno));
- shutdown(sock, SHUT_RDWR);
+ debug2("bind port %d: %.100s", port, strerror(errno));
close(sock);
if (ai->ai_next)
continue;
for (n = 0; n < num_socks; n++) {
- shutdown(socks[n], SHUT_RDWR);
close(socks[n]);
}
num_socks = 0;
if (num_socks == NUM_SOCKS)
break;
#else
- break;
+ if (x11_use_localhost) {
+ if (num_socks == NUM_SOCKS)
+ break;
+ } else {
+ break;
+ }
#endif
}
freeaddrinfo(aitop);
}
if (display_number >= MAX_DISPLAYS) {
error("Failed to allocate internet-domain X11 display socket.");
- return NULL;
+ return -1;
}
/* Start listening for connections on the socket. */
for (n = 0; n < num_socks; n++) {
sock = socks[n];
- if (listen(sock, 5) < 0) {
+ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
error("listen: %.100s", strerror(errno));
- shutdown(sock, SHUT_RDWR);
- close(sock);
- return NULL;
- }
- }
-
- /* Set up a suitable value for the DISPLAY variable. */
- if (gethostname(hostname, sizeof(hostname)) < 0)
- fatal("gethostname: %.100s", strerror(errno));
-
-#ifdef IPADDR_IN_DISPLAY
- /*
- * HPUX detects the local hostname in the DISPLAY variable and tries
- * to set up a shared memory connection to the server, which it
- * incorrectly supposes to be local.
- *
- * The workaround - as used in later $$H and other programs - is
- * is to set display to the host's IP address.
- */
- {
- struct hostent *he;
- struct in_addr my_addr;
-
- he = gethostbyname(hostname);
- if (he == NULL) {
- error("[X11-broken-fwd-hostname-workaround] Could not get "
- "IP address for hostname %s.", hostname);
-
- packet_send_debug("[X11-broken-fwd-hostname-workaround]"
- "Could not get IP address for hostname %s.", hostname);
-
- shutdown(sock, SHUT_RDWR);
close(sock);
-
- return NULL;
+ return -1;
}
-
- memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr));
-
- /* Set DISPLAY to <ip address>:screen.display */
- snprintf(display, sizeof(display), "%.50s:%d.%d", inet_ntoa(my_addr),
- display_number, screen_number);
}
-#else /* IPADDR_IN_DISPLAY */
- /* Just set DISPLAY to hostname:screen.display */
- snprintf(display, sizeof display, "%.400s:%d.%d", hostname,
- display_number, screen_number);
-#endif /* IPADDR_IN_DISPLAY */
/* Allocate a channel for each socket. */
+ if (chanids != NULL)
+ *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1));
for (n = 0; n < num_socks; n++) {
sock = socks[n];
- (void) channel_new("x11 listener",
+ nc = channel_new("x11 listener",
SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
- 0, xstrdup("X11 inet listener"), 1);
+ 0, "X11 inet listener", 1);
+ nc->single_connection = single_connection;
+ if (*chanids != NULL)
+ (*chanids)[n] = nc->self;
}
+ if (*chanids != NULL)
+ (*chanids)[n] = -1;
- /* Return a suitable value for the DISPLAY environment variable. */
- return xstrdup(display);
+ /* Return the display number for the DISPLAY environment variable. */
+ *display_numberp = display_number;
+ return (0);
}
-#ifndef X_UNIX_PATH
-#define X_UNIX_PATH "/tmp/.X11-unix/X"
-#endif
-
static int
connect_local_xsocket(u_int dnr)
{
- static const char *const x_sockets[] = {
- X_UNIX_PATH "%u",
- "/var/X/.X11-unix/X" "%u",
- "/usr/spool/sockets/X11/" "%u",
- NULL
- };
int sock;
struct sockaddr_un addr;
- const char *const * path;
- for (path = x_sockets; *path; ++path) {
- sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- error("socket: %.100s", strerror(errno));
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr);
- if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
- return sock;
- close(sock);
- }
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ error("socket: %.100s", strerror(errno));
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);
+ if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
+ return sock;
+ close(sock);
error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
return -1;
}
/* Connect to the unix domain socket. */
if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
error("Could not parse display number from DISPLAY: %.100s",
- display);
+ display);
return -1;
}
/* Create a socket. */
* Connect to an inet socket. The DISPLAY value is supposedly
* hostname:d[.s], where hostname may also be numeric IP address.
*/
- strncpy(buf, display, sizeof(buf));
- buf[sizeof(buf) - 1] = 0;
+ strlcpy(buf, display, sizeof(buf));
cp = strchr(buf, ':');
if (!cp) {
error("Could not find ':' in DISPLAY: %.100s", display);
/* buf now contains the host name. But first we parse the display number. */
if (sscanf(cp + 1, "%d", &display_number) != 1) {
error("Could not parse display number from DISPLAY: %.100s",
- display);
+ display);
return -1;
}
}
for (ai = aitop; ai; ai = ai->ai_next) {
/* Create a socket. */
- sock = socket(ai->ai_family, SOCK_STREAM, 0);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
- debug("socket: %.100s", strerror(errno));
+ debug2("socket: %.100s", strerror(errno));
continue;
}
/* Connect it to the display. */
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
- debug("connect %.100s port %d: %.100s", buf,
+ debug2("connect %.100s port %d: %.100s", buf,
6000 + display_number, strerror(errno));
close(sock);
continue;
strerror(errno));
return -1;
}
+ set_nodelay(sock);
return sock;
}
*/
void
-x11_input_open(int type, int plen, void *ctxt)
+x11_input_open(int type, u_int32_t seq, void *ctxt)
{
Channel *c = NULL;
int remote_id, sock = 0;
} else {
remote_host = xstrdup("unknown (remote did not supply name)");
}
- packet_done();
+ packet_check_eom();
/* Obtain a connection to the real X display. */
sock = x11_connect_display();
c = channel_new("connected x11 socket",
SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
remote_host, 1);
- if (c == NULL) {
- error("x11_input_open: channel_new failed");
- close(sock);
- } else {
- c->remote_id = remote_id;
- c->force_drain = 1;
- }
+ c->remote_id = remote_id;
+ c->force_drain = 1;
}
+ xfree(remote_host);
if (c == NULL) {
/* Send refusal to the remote host. */
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
/* dummy protocol handler that denies SSH-1 requests (agent/x11) */
void
-deny_input_open(int type, int plen, void *ctxt)
+deny_input_open(int type, u_int32_t seq, void *ctxt)
{
int rchan = packet_get_int();
- switch(type){
+
+ switch (type) {
case SSH_SMSG_AGENT_OPEN:
error("Warning: ssh server tried agent forwarding.");
break;
error("Warning: ssh server tried X11 forwarding.");
break;
default:
- error("deny_input_open: type %d plen %d", type, plen);
+ error("deny_input_open: type %d", type);
break;
}
error("Warning: this is probably a break in attempt by a malicious server.");
* This should be called in the client only.
*/
void
-x11_request_forwarding_with_spoofing(int client_session_id,
+x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
const char *proto, const char *data)
{
u_int data_len = (u_int) strlen(data) / 2;
- u_int i, value, len;
+ u_int i, value;
char *new_data;
int screen_number;
const char *cp;
- u_int32_t rand = 0;
+ u_int32_t rnd = 0;
- cp = getenv("DISPLAY");
- if (cp)
- cp = strchr(cp, ':');
+ if (x11_saved_display == NULL)
+ x11_saved_display = xstrdup(disp);
+ else if (strcmp(disp, x11_saved_display) != 0) {
+ error("x11_request_forwarding_with_spoofing: different "
+ "$DISPLAY already forwarded");
+ return;
+ }
+
+ cp = disp;
+ if (disp)
+ cp = strchr(disp, ':');
if (cp)
cp = strchr(cp, '.');
if (cp)
else
screen_number = 0;
- /* Save protocol name. */
- x11_saved_proto = xstrdup(proto);
-
- /*
- * Extract real authentication data and generate fake data of the
- * same length.
- */
- x11_saved_data = xmalloc(data_len);
- x11_fake_data = xmalloc(data_len);
- for (i = 0; i < data_len; i++) {
- if (sscanf(data + 2 * i, "%2x", &value) != 1)
- fatal("x11_request_forwarding: bad authentication data: %.100s", data);
- if (i % 4 == 0)
- rand = arc4random();
- x11_saved_data[i] = value;
- x11_fake_data[i] = rand & 0xff;
- rand >>= 8;
- }
- x11_saved_data_len = data_len;
- x11_fake_data_len = data_len;
+ if (x11_saved_proto == NULL) {
+ /* Save protocol name. */
+ x11_saved_proto = xstrdup(proto);
+ /*
+ * Extract real authentication data and generate fake data
+ * of the same length.
+ */
+ x11_saved_data = xmalloc(data_len);
+ x11_fake_data = xmalloc(data_len);
+ for (i = 0; i < data_len; i++) {
+ if (sscanf(data + 2 * i, "%2x", &value) != 1)
+ fatal("x11_request_forwarding: bad "
+ "authentication data: %.100s", data);
+ if (i % 4 == 0)
+ rnd = arc4random();
+ x11_saved_data[i] = value;
+ x11_fake_data[i] = rnd & 0xff;
+ rnd >>= 8;
+ }
+ x11_saved_data_len = data_len;
+ x11_fake_data_len = data_len;
+ }
/* Convert the fake data into hex. */
- len = 2 * data_len + 1;
- new_data = xmalloc(len);
- for (i = 0; i < data_len; i++)
- snprintf(new_data + 2 * i, len - 2 * i,
- "%02x", (u_char) x11_fake_data[i]);
+ new_data = tohex(x11_fake_data, data_len);
/* Send the request packet. */
if (compat20) {
/* Sends a message to the server to request authentication fd forwarding. */
void
-auth_request_forwarding()
+auth_request_forwarding(void)
{
packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
packet_send();
packet_write_wait();
}
-
-/*
- * Returns the name of the forwarded authentication socket. Returns NULL if
- * there is no forwarded authentication socket. The returned value points to
- * a static buffer.
- */
-
-char *
-auth_get_socket_name()
-{
- return auth_sock_name;
-}
-
-/* removes the agent forwarding socket */
-
-void
-auth_sock_cleanup_proc(void *_pw)
-{
- struct passwd *pw = _pw;
-
- if (auth_sock_name) {
- temporarily_use_uid(pw);
- unlink(auth_sock_name);
- rmdir(auth_sock_dir);
- auth_sock_name = NULL;
- restore_uid();
- }
-}
-
-/*
- * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
- * This starts forwarding authentication requests.
- */
-
-int
-auth_input_request_forwarding(struct passwd * pw)
-{
- Channel *nc;
- int sock;
- struct sockaddr_un sunaddr;
-
- if (auth_get_socket_name() != NULL) {
- error("authentication forwarding requested twice.");
- return 0;
- }
-
- /* Temporarily drop privileged uid for mkdir/bind. */
- temporarily_use_uid(pw);
-
- /* Allocate a buffer for the socket name, and format the name. */
- auth_sock_name = xmalloc(MAXPATHLEN);
- auth_sock_dir = xmalloc(MAXPATHLEN);
- strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
-
- /* Create private directory for socket */
- if (mkdtemp(auth_sock_dir) == NULL) {
- packet_send_debug("Agent forwarding disabled: "
- "mkdtemp() failed: %.100s", strerror(errno));
- restore_uid();
- xfree(auth_sock_name);
- xfree(auth_sock_dir);
- auth_sock_name = NULL;
- auth_sock_dir = NULL;
- return 0;
- }
- snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%d",
- auth_sock_dir, (int) getpid());
-
- /* delete agent socket on fatal() */
- fatal_add_cleanup(auth_sock_cleanup_proc, pw);
-
- /* Create the socket. */
- sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- packet_disconnect("socket: %.100s", strerror(errno));
-
- /* Bind it to the name. */
- memset(&sunaddr, 0, sizeof(sunaddr));
- sunaddr.sun_family = AF_UNIX;
- strncpy(sunaddr.sun_path, auth_sock_name,
- sizeof(sunaddr.sun_path));
-
- if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0)
- packet_disconnect("bind: %.100s", strerror(errno));
-
- /* Restore the privileged uid. */
- restore_uid();
-
- /* Start listening on the socket. */
- if (listen(sock, 5) < 0)
- packet_disconnect("listen: %.100s", strerror(errno));
-
- /* Allocate a channel for the authentication agent socket. */
- nc = channel_new("auth socket",
- SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
- CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
- 0, xstrdup("auth socket"), 1);
- if (nc == NULL) {
- error("auth_input_request_forwarding: channel_new failed");
- auth_sock_cleanup_proc(pw);
- fatal_remove_cleanup(auth_sock_cleanup_proc, pw);
- close(sock);
- return 0;
- }
- strlcpy(nc->path, auth_sock_name, sizeof(nc->path));
- return 1;
-}
-
-/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
-
-void
-auth_input_open_request(int type, int plen, void *ctxt)
-{
- Channel *c = NULL;
- int remote_id, sock;
- char *name;
-
- packet_integrity_check(plen, 4, type);
-
- /* Read the remote channel number from the message. */
- remote_id = packet_get_int();
-
- /*
- * Get a connection to the local authentication agent (this may again
- * get forwarded).
- */
- sock = ssh_get_authentication_socket();
-
- /*
- * If we could not connect the agent, send an error message back to
- * the server. This should never happen unless the agent dies,
- * because authentication forwarding is only enabled if we have an
- * agent.
- */
- if (sock >= 0) {
- name = xstrdup("authentication agent connection");
- c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
- -1, 0, 0, 0, name, 1);
- if (c == NULL) {
- error("auth_input_open_request: channel_new failed");
- xfree(name);
- close(sock);
- } else {
- c->remote_id = remote_id;
- c->force_drain = 1;
- }
- }
- if (c == NULL) {
- packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
- packet_put_int(remote_id);
- } else {
- /* Send a confirmation to the remote host. */
- debug("Forwarding authentication connection.");
- packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
- packet_put_int(remote_id);
- packet_put_int(c->self);
- }
- packet_send();
-}
+/* $OpenBSD: channels.h,v 1.79 2005/07/17 06:49:04 djm Exp $ */
+
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* called by a name other than "ssh" or "Secure Shell".
*/
/*
- * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/* RCSID("$OpenBSD: channels.h,v 1.51 2001/11/07 22:53:21 markus Exp $"); */
#ifndef CHANNEL_H
#define CHANNEL_H
int type; /* channel type/state */
int self; /* my own channel identifier */
int remote_id; /* channel identifier for remote peer */
- int istate; /* input from channel (state of receive half) */
- int ostate; /* output to channel (state of transmit half) */
+ u_int istate; /* input from channel (state of receive half) */
+ u_int ostate; /* output to channel (state of transmit half) */
int flags; /* close sent/rcvd */
int rfd; /* read fd */
int wfd; /* write fd */
int efd; /* extended fd */
int sock; /* sock fd */
+ int ctl_fd; /* control fd (client sharing) */
int isatty; /* rfd is a tty */
+ int wfd_isatty; /* wfd is a tty */
+ int client_tty; /* (client) TTY has been requested */
int force_drain; /* force close on iEOF */
int delayed; /* fdset hack */
Buffer input; /* data read from socket, to be sent over
int host_port; /* remote port to connect for forwards */
char *remote_name; /* remote hostname */
- int remote_window;
- int remote_maxpacket;
- int local_window;
- int local_window_max;
- int local_consumed;
- int local_maxpacket;
+ u_int remote_window;
+ u_int remote_maxpacket;
+ u_int local_window;
+ u_int local_window_max;
+ u_int local_consumed;
+ u_int local_maxpacket;
+ int dynamic_window;
int extended_usage;
+ int single_connection;
char *ctype; /* type */
/* callback */
- channel_callback_fn *cb_fn;
- void *cb_arg;
- int cb_event;
+ channel_callback_fn *confirm;
channel_callback_fn *detach_user;
+ void *confirm_ctx;
/* filter */
channel_filter_fn *input_filter;
#define CHAN_EXTENDED_WRITE 2
/* default window/packet sizes for tcp/x11-fwd-channel */
-#define CHAN_SES_WINDOW_DEFAULT (32*1024)
-#define CHAN_SES_PACKET_DEFAULT (CHAN_SES_WINDOW_DEFAULT/2)
-#define CHAN_TCP_WINDOW_DEFAULT (32*1024)
-#define CHAN_TCP_PACKET_DEFAULT (CHAN_TCP_WINDOW_DEFAULT/2)
-#define CHAN_X11_WINDOW_DEFAULT (4*1024)
-#define CHAN_X11_PACKET_DEFAULT (CHAN_X11_WINDOW_DEFAULT/2)
+#define CHAN_SES_PACKET_DEFAULT (32*1024)
+#define CHAN_SES_WINDOW_DEFAULT (0xa00000/2)
+#define CHAN_TCP_PACKET_DEFAULT (32*1024)
+#define CHAN_TCP_WINDOW_DEFAULT (0xa00000/2)
+#define CHAN_X11_PACKET_DEFAULT (16*1024)
+#define CHAN_X11_WINDOW_DEFAULT (0xa00000/2)
/* possible input states */
-#define CHAN_INPUT_OPEN 0x01
-#define CHAN_INPUT_WAIT_DRAIN 0x02
-#define CHAN_INPUT_WAIT_OCLOSE 0x04
-#define CHAN_INPUT_CLOSED 0x08
+#define CHAN_INPUT_OPEN 0
+#define CHAN_INPUT_WAIT_DRAIN 1
+#define CHAN_INPUT_WAIT_OCLOSE 2
+#define CHAN_INPUT_CLOSED 3
/* possible output states */
-#define CHAN_OUTPUT_OPEN 0x10
-#define CHAN_OUTPUT_WAIT_DRAIN 0x20
-#define CHAN_OUTPUT_WAIT_IEOF 0x40
-#define CHAN_OUTPUT_CLOSED 0x80
+#define CHAN_OUTPUT_OPEN 0
+#define CHAN_OUTPUT_WAIT_DRAIN 1
+#define CHAN_OUTPUT_WAIT_IEOF 2
+#define CHAN_OUTPUT_CLOSED 3
#define CHAN_CLOSE_SENT 0x01
#define CHAN_CLOSE_RCVD 0x02
+#define CHAN_EOF_SENT 0x04
+#define CHAN_EOF_RCVD 0x08
+
+/* check whether 'efd' is still in use */
+#define CHANNEL_EFD_INPUT_ACTIVE(c) \
+ (compat20 && c->extended_usage == CHAN_EXTENDED_READ && \
+ (c->efd != -1 || \
+ buffer_len(&c->extended) > 0))
+#define CHANNEL_EFD_OUTPUT_ACTIVE(c) \
+ (compat20 && c->extended_usage == CHAN_EXTENDED_WRITE && \
+ c->efd != -1 && (!(c->flags & (CHAN_EOF_RCVD|CHAN_CLOSE_RCVD)) || \
+ buffer_len(&c->extended) > 0))
/* channel management */
Channel *channel_lookup(int);
-Channel *channel_new(char *, int, int, int, int, int, int, int, char *, int);
-void channel_set_fds(int, int, int, int, int, int);
+Channel *channel_new(char *, int, int, int, int, u_int, u_int, int, char *, int);
+void channel_set_fds(int, int, int, int, int, int, u_int);
void channel_free(Channel *);
void channel_free_all(void);
void channel_stop_listening(void);
void channel_send_open(int);
-void channel_request(int, char *, int);
void channel_request_start(int, char *, int);
-void channel_register_callback(int, int mtype, channel_callback_fn *, void *);
void channel_register_cleanup(int, channel_callback_fn *);
+void channel_register_confirm(int, channel_callback_fn *, void *);
void channel_register_filter(int, channel_filter_fn *);
void channel_cancel_cleanup(int);
int channel_close_fd(int *);
+void channel_send_window_changes(void);
/* protocol handler */
-void channel_input_channel_request(int, int, void *);
-void channel_input_close(int, int, void *);
-void channel_input_close_confirmation(int, int, void *);
-void channel_input_data(int, int, void *);
-void channel_input_extended_data(int, int, void *);
-void channel_input_ieof(int, int, void *);
-void channel_input_oclose(int, int, void *);
-void channel_input_open_confirmation(int, int, void *);
-void channel_input_open_failure(int, int, void *);
-void channel_input_port_open(int, int, void *);
-void channel_input_window_adjust(int, int, void *);
+void channel_input_close(int, u_int32_t, void *);
+void channel_input_close_confirmation(int, u_int32_t, void *);
+void channel_input_data(int, u_int32_t, void *);
+void channel_input_extended_data(int, u_int32_t, void *);
+void channel_input_ieof(int, u_int32_t, void *);
+void channel_input_oclose(int, u_int32_t, void *);
+void channel_input_open_confirmation(int, u_int32_t, void *);
+void channel_input_open_failure(int, u_int32_t, void *);
+void channel_input_port_open(int, u_int32_t, void *);
+void channel_input_window_adjust(int, u_int32_t, void *);
/* file descriptor handling (read/write) */
-void channel_prepare_select(fd_set **, fd_set **, int *, int*, int);
+void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, int);
void channel_after_select(fd_set *, fd_set *);
void channel_output_poll(void);
void channel_input_port_forward_request(int, int);
int channel_connect_to(const char *, u_short);
int channel_connect_by_listen_address(u_short);
-void channel_request_remote_forwarding(u_short, const char *, u_short);
-int channel_request_local_forwarding(u_short, const char *, u_short, int);
-int
-channel_request_forwarding(const char *, u_short, const char *, u_short, int,
- int);
+void channel_request_remote_forwarding(const char *, u_short,
+ const char *, u_short);
+int channel_setup_local_fwd_listener(const char *, u_short,
+ const char *, u_short, int);
+void channel_request_rforward_cancel(const char *host, u_short port);
+int channel_setup_remote_fwd_listener(const char *, u_short, int);
+int channel_cancel_rport_listener(const char *, u_short);
/* x11 forwarding */
int x11_connect_display(void);
-char *x11_create_display(int);
-char *x11_create_display_inet(int, int);
-void x11_input_open(int, int, void *);
-void x11_request_forwarding(void);
-void x11_request_forwarding_with_spoofing(int, const char *, const char *);
-void deny_input_open(int, int, void *);
+int x11_create_display_inet(int, int, int, u_int *, int **);
+void x11_input_open(int, u_int32_t, void *);
+void x11_request_forwarding_with_spoofing(int, const char *, const char *,
+ const char *);
+void deny_input_open(int, u_int32_t, void *);
/* agent forwarding */
void auth_request_forwarding(void);
-char *auth_get_socket_name(void);
-void auth_sock_cleanup_proc(void *);
-int auth_input_request_forwarding(struct passwd *);
-void auth_input_open_request(int, int, void *);
/* channel close */
int chan_is_dead(Channel *, int);
void chan_mark_dead(Channel *);
-void chan_init_iostates(Channel *);
-void chan_init(void);
-typedef void chan_event_fn(Channel *);
+/* channel events */
-/* for the input state */
-extern chan_event_fn *chan_rcvd_oclose;
-extern chan_event_fn *chan_read_failed;
-extern chan_event_fn *chan_ibuf_empty;
+void chan_rcvd_oclose(Channel *);
+void chan_read_failed(Channel *);
+void chan_ibuf_empty(Channel *);
-/* for the output state */
-extern chan_event_fn *chan_rcvd_ieof;
-extern chan_event_fn *chan_write_failed;
-extern chan_event_fn *chan_obuf_empty;
+void chan_rcvd_ieof(Channel *);
+void chan_write_failed(Channel *);
+void chan_obuf_empty(Channel *);
#endif
for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
(p = strsep(&cp, CIPHER_SEP))) {
c = cipher_by_name(p);
- if (c == NULL || c->number != SSH_CIPHER_SSH2) {
+ if (c == NULL || (c->number != SSH_CIPHER_SSH2 &&
+c->number != SSH_CIPHER_NONE)) {
debug("bad cipher %s [%s]", p, names);
xfree(cipher_list);
return 0;
int evplen;
switch (c->number) {
+ case SSH_CIPHER_NONE:
case SSH_CIPHER_SSH2:
case SSH_CIPHER_DES:
case SSH_CIPHER_BLOWFISH:
int evplen = 0;
switch (c->number) {
+ case SSH_CIPHER_NONE:
case SSH_CIPHER_SSH2:
case SSH_CIPHER_DES:
case SSH_CIPHER_BLOWFISH:
strlen(check[i].pat), 0) == 1) {
debug("match: %s pat %s", version, check[i].pat);
datafellows = check[i].bugs;
+ /* Check to see if the remote side is OpenSSH and not HPN */
+ if(strstr(version,"OpenSSH") != NULL)
+ {
+ if (strstr(version,"hpn") == NULL)
+ {
+ datafellows |= SSH_BUG_LARGEWINDOW;
+ }
+ }
return;
}
}
#define SSH_BUG_PROBE 0x00400000
#define SSH_BUG_FIRSTKEX 0x00800000
#define SSH_OLD_FORWARD_ADDR 0x01000000
+#define SSH_BUG_LARGEWINDOW 0x02000000
void enable_compat13(void);
void enable_compat20(void);
static void kex_choose_conf(Kex *);
/* put algorithm proposal into buffer */
-static void
+void
kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
{
u_int i;
void (*kex[KEX_MAX])(Kex *);
};
+void kex_prop2buf(Buffer *, char *proposal[PROPOSAL_MAX]);
+
Kex *kex_setup(char *[PROPOSAL_MAX]);
void kex_finish(Kex *);
-/* $OpenBSD: myproposal.h,v 1.12 2001/03/05 15:56:16 deraadt Exp $ */
+/* $OpenBSD: myproposal.h,v 1.18 2005/07/25 11:59:39 markus Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define KEX_DEFAULT_KEX "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1"
+#define KEX_DEFAULT_KEX "diffie-hellman-group-exchange-sha1," \
+ "diffie-hellman-group14-sha1," \
+ "diffie-hellman-group1-sha1"
#define KEX_DEFAULT_PK_ALG "ssh-rsa,ssh-dss"
#define KEX_DEFAULT_ENCRYPT \
- "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour," \
- "aes192-cbc,aes256-cbc," \
- "rijndael128-cbc,rijndael192-cbc,rijndael256-cbc," \
- "rijndael-cbc@lysator.liu.se"
+ "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \
+ "arcfour128,arcfour256,arcfour," \
+ "aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se," \
+ "aes128-ctr,aes192-ctr,aes256-ctr,none"
#define KEX_DEFAULT_MAC \
"hmac-md5,hmac-sha1,hmac-ripemd160," \
"hmac-ripemd160@openssh.com," \
"hmac-sha1-96,hmac-md5-96"
-#define KEX_DEFAULT_COMP "none,zlib"
+#define KEX_DEFAULT_COMP "none,zlib@openssh.com,zlib"
#define KEX_DEFAULT_LANG ""
rnd >>= 8;
}
}
+int rekey_requested = 0;
+
+void
+packet_request_rekeying(void)
+{
+ rekey_requested = 1;
+}
#define MAX_PACKETS (1U<<31)
int
{
if (datafellows & SSH_BUG_NOREKEY)
return 0;
+ if (rekey_requested == 1)
+ {
+ rekey_requested = 0;
+ return 1;
+ }
return
(p_send.packets > MAX_PACKETS) ||
(p_read.packets > MAX_PACKETS) ||
#include <openssl/bn.h>
+void
+packet_request_rekeying(void);
+
void packet_set_connection(int, int);
void packet_set_nonblocking(void);
int packet_get_connection_in(void);
options->verify_host_key_dns = -1;
options->server_alive_interval = -1;
options->server_alive_count_max = -1;
+ options->none_switch = -1;
options->num_send_env = 0;
options->control_path = NULL;
options->control_master = -1;
options->server_alive_interval = 0;
if (options->server_alive_count_max == -1)
options->server_alive_count_max = 3;
+ if (options->none_switch == -1)
+ options->none_switch = 0;
if (options->control_master == -1)
options->control_master = 0;
if (options->hash_known_hosts == -1)
int compression_level; /* Compression level 1 (fast) to 9
* (best). */
int tcp_keep_alive; /* Set SO_KEEPALIVE. */
+ int tcp_rcv_buf; /* user switch to set tcp recv buffer */
LogLevel log_level; /* Level for logging. */
int port; /* Port to connect. */
int enable_ssh_keysign;
int rekey_limit;
+ int none_switch;
int no_host_authentication_for_localhost;
int identities_only;
int server_alive_interval;
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*/
#include "includes.h"
-RCSID("$OpenBSD: scp.c,v 1.85 2001/10/01 08:06:28 markus Exp $");
+RCSID("$OpenBSD: scp.c,v 1.125 2005/07/27 10:39:03 dtucker Exp $");
#include "xmalloc.h"
#include "atomicio.h"
#include "pathnames.h"
#include "log.h"
#include "misc.h"
+#include "progressmeter.h"
-#ifdef HAVE___PROGNAME
extern char *__progname;
-#else
-char *__progname;
-#endif
-
-/* For progressmeter() -- number of seconds before xfer considered "stalled" */
-#define STALLTIME 5
-/* alarm() interval for updating progress meter */
-#define PROGRESSTIME 1
-
-/* Progress meter bar */
-#define BAR \
- "************************************************************"\
- "************************************************************"\
- "************************************************************"\
- "************************************************************"
-#define MAX_BARLENGTH (sizeof(BAR) - 1)
-
-/* Visual statistics about files as they are transferred. */
-void progressmeter(int);
-/* Returns width of the terminal (for progress meter calculations). */
-int getttywidth(void);
-int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc);
+void bwlimit(int);
/* Struct for addargs */
arglist args;
-/* Time a transfer started. */
-static struct timeval start;
-
-/* Number of bytes of current file transferred so far. */
-volatile off_t statbytes;
-
-/* Total size of current file. */
-off_t totalbytes = 0;
+/* Bandwidth limit */
+off_t limit_rate = 0;
/* Name of current file being transferred. */
char *curfile;
/* This is the program to execute for the secured connection. ("ssh" or -S) */
char *ssh_program = _PATH_SSH_PROGRAM;
+/* This is used to store the pid of ssh_program */
+pid_t do_cmd_pid = -1;
+
+static void
+killchild(int signo)
+{
+ if (do_cmd_pid > 1) {
+ kill(do_cmd_pid, signo ? signo : SIGTERM);
+ waitpid(do_cmd_pid, NULL, 0);
+ }
+
+ if (signo)
+ _exit(1);
+ exit(1);
+}
+
/*
* This function executes the given command as the specified user on the
* given host. This returns < 0 if execution fails, and >= 0 otherwise. This
close(reserved[0]);
close(reserved[1]);
- /* For a child to execute the command on the remote host using ssh. */
- if (fork() == 0) {
+ /* Fork a child to execute the command on the remote host using ssh. */
+ do_cmd_pid = fork();
+ if (do_cmd_pid == 0) {
/* Child. */
close(pin[1]);
close(pout[0]);
execvp(ssh_program, args.list);
perror(ssh_program);
exit(1);
+ } else if (do_cmd_pid == -1) {
+ fatal("fork: %s", strerror(errno));
}
/* Parent. Close the other side, and return the local side. */
close(pin[0]);
*fdout = pin[1];
close(pout[1]);
*fdin = pout[0];
+ signal(SIGTERM, killchild);
+ signal(SIGINT, killchild);
+ signal(SIGHUP, killchild);
return 0;
}
typedef struct {
- int cnt;
+ size_t cnt;
char *buf;
} BUF;
void usage(void);
int
-main(argc, argv)
- int argc;
- char *argv[];
+main(int argc, char **argv)
{
- int ch, fflag, tflag;
- char *targ;
+ int ch, fflag, tflag, status;
+ double speed;
+ char *targ, *endp;
extern char *optarg;
extern int optind;
- __progname = get_progname(argv[0]);
+ __progname = ssh_get_progname(argv[0]);
args.list = NULL;
- addargs(&args, "ssh"); /* overwritten with ssh_program */
+ addargs(&args, "ssh"); /* overwritten with ssh_program */
addargs(&args, "-x");
addargs(&args, "-oForwardAgent no");
- addargs(&args, "-oFallBackToRsh no");
addargs(&args, "-oClearAllForwardings yes");
fflag = tflag = 0;
- while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1)
+ while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246zS:o:F:w:")) != -1)
switch (ch) {
/* User-visible flags. */
+ case '1':
+ case '2':
case '4':
case '6':
case 'C':
+ case 'z':
addargs(&args, "-%c", ch);
break;
case 'o':
case 'B':
addargs(&args, "-oBatchmode yes");
break;
+ case 'l':
+ speed = strtod(optarg, &endp);
+ if (speed <= 0 || *endp != '\0')
+ usage();
+ limit_rate = speed * 1024;
+ break;
case 'p':
pflag = 1;
break;
verbose_mode = 1;
break;
case 'q':
+ addargs(&args, "-q");
showprogress = 0;
break;
setmode(0, O_BINARY);
#endif
break;
+ case 'w':
+ addargs(&args, "-w%s", optarg);
+ break;
default:
usage();
}
argv += optind;
if ((pwd = getpwuid(userid = getuid())) == NULL)
- fatal("unknown user %d", (int) userid);
+ fatal("unknown user %u", (u_int) userid);
if (!isatty(STDERR_FILENO))
showprogress = 0;
targetshouldbedirectory = 1;
remin = remout = -1;
+ do_cmd_pid = -1;
/* Command to be executed on remote system using "ssh". */
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
verbose_mode ? " -v" : "",
if (targetshouldbedirectory)
verifydir(argv[argc - 1]);
}
+ /*
+ * Finally check the exit status of the ssh process, if one was forked
+ * and no error has occured yet
+ */
+ if (do_cmd_pid != -1 && errs == 0) {
+ if (remin != -1)
+ (void) close(remin);
+ if (remout != -1)
+ (void) close(remout);
+ if (waitpid(do_cmd_pid, &status, 0) == -1)
+ errs = 1;
+ else {
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ errs = 1;
+ }
+ }
exit(errs != 0);
}
void
-toremote(targ, argc, argv)
- char *targ, *argv[];
- int argc;
+toremote(char *targ, int argc, char **argv)
{
int i, len;
- char *bp, *host, *src, *suser, *thost, *tuser;
+ char *bp, *host, *src, *suser, *thost, *tuser, *arg;
*targ++ = 0;
if (*targ == 0)
targ = ".";
- if ((thost = strchr(argv[argc - 1], '@'))) {
+ arg = xstrdup(argv[argc - 1]);
+ if ((thost = strrchr(arg, '@'))) {
/* user@host */
*thost++ = 0;
- tuser = argv[argc - 1];
+ tuser = arg;
if (*tuser == '\0')
tuser = NULL;
- else if (!okname(tuser))
- exit(1);
} else {
- thost = argv[argc - 1];
+ thost = arg;
tuser = NULL;
}
src = colon(argv[i]);
if (src) { /* remote to remote */
static char *ssh_options =
- "-x -o'FallBackToRsh no' "
- "-o'ClearAllForwardings yes'";
+ "-x -o'ClearAllForwardings yes'";
*src++ = 0;
if (*src == 0)
src = ".";
- host = strchr(argv[i], '@');
+ host = strrchr(argv[i], '@');
len = strlen(ssh_program) + strlen(argv[i]) +
strlen(src) + (tuser ? strlen(tuser) : 0) +
strlen(thost) + strlen(targ) +
suser = argv[i];
if (*suser == '\0')
suser = pwd->pw_name;
- else if (!okname(suser))
+ else if (!okname(suser)) {
+ xfree(bp);
continue;
+ }
+ if (tuser && !okname(tuser)) {
+ xfree(bp);
+ continue;
+ }
snprintf(bp, len,
"%s%s %s -n "
"-l %s %s %s %s '%s%s%s:%s'",
}
if (verbose_mode)
fprintf(stderr, "Executing: %s\n", bp);
- (void) system(bp);
+ if (system(bp) != 0)
+ errs = 1;
(void) xfree(bp);
} else { /* local to remote */
if (remin == -1) {
}
void
-tolocal(argc, argv)
- int argc;
- char *argv[];
+tolocal(int argc, char **argv)
{
int i, len;
char *bp, *host, *src, *suser;
*src++ = 0;
if (*src == 0)
src = ".";
- if ((host = strchr(argv[i], '@')) == NULL) {
+ if ((host = strrchr(argv[i], '@')) == NULL) {
host = argv[i];
suser = NULL;
} else {
suser = argv[i];
if (*suser == '\0')
suser = pwd->pw_name;
- else if (!okname(suser))
- continue;
}
host = cleanhostname(host);
len = strlen(src) + CMDNEEDS + 20;
}
void
-source(argc, argv)
- int argc;
- char *argv[];
+source(int argc, char **argv)
{
struct stat stb;
static BUF buffer;
BUF *bp;
- off_t i, amt, result;
- int fd, haderr, indx;
- char *last, *name, buf[2048];
+ off_t i, amt, statbytes;
+ size_t result;
+ int fd = -1, haderr, indx;
+ char *last, *name, buf[16384];
int len;
for (indx = 0; indx < argc; ++indx) {
(void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n",
(u_long) stb.st_mtime,
(u_long) stb.st_atime);
- (void) atomicio(write, remout, buf, strlen(buf));
+ (void) atomicio(vwrite, remout, buf, strlen(buf));
if (response() < 0)
goto next;
}
#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
-#ifdef HAVE_LONG_LONG_INT
snprintf(buf, sizeof buf, "C%04o %lld %s\n",
(u_int) (stb.st_mode & FILEMODEMASK),
- (long long) stb.st_size, last);
-#else
- /* XXX: Handle integer overflow? */
- snprintf(buf, sizeof buf, "C%04o %lu %s\n",
- (u_int) (stb.st_mode & FILEMODEMASK),
- (u_long) stb.st_size, last);
-#endif
-
+ (int64_t)stb.st_size, last);
if (verbose_mode) {
fprintf(stderr, "Sending file modes: %s", buf);
- fflush(stderr);
}
- (void) atomicio(write, remout, buf, strlen(buf));
+ (void) atomicio(vwrite, remout, buf, strlen(buf));
if (response() < 0)
goto next;
- if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
+ /* this change decreases the number of read/write syscalls*/
+ /* when scp acts as data source. this is the critical change*/
+ /* buf can actually remain at 2k but increasing both to 16k*/
+ /* seemed to make sense*/
+ if ((bp = allocbuf(&buffer, fd, sizeof(buf))) == NULL) {
next: (void) close(fd);
continue;
}
- if (showprogress) {
- totalbytes = stb.st_size;
- progressmeter(-1);
- }
+ if (showprogress)
+ start_progress_meter(curfile, stb.st_size, &statbytes);
/* Keep writing after an error so that we stay sync'd up. */
for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
amt = bp->cnt;
if (!haderr) {
result = atomicio(read, fd, bp->buf, amt);
if (result != amt)
- haderr = result >= 0 ? EIO : errno;
+ haderr = errno;
}
if (haderr)
- (void) atomicio(write, remout, bp->buf, amt);
+ (void) atomicio(vwrite, remout, bp->buf, amt);
else {
- result = atomicio(write, remout, bp->buf, amt);
+ result = atomicio(vwrite, remout, bp->buf, amt);
if (result != amt)
- haderr = result >= 0 ? EIO : errno;
+ haderr = errno;
statbytes += result;
}
+ if (limit_rate)
+ bwlimit(amt);
}
if (showprogress)
- progressmeter(1);
+ stop_progress_meter();
if (close(fd) < 0 && !haderr)
haderr = errno;
if (!haderr)
- (void) atomicio(write, remout, "", 1);
+ (void) atomicio(vwrite, remout, "", 1);
else
run_err("%s: %s", name, strerror(haderr));
(void) response();
}
void
-rsource(name, statp)
- char *name;
- struct stat *statp;
+rsource(char *name, struct stat *statp)
{
DIR *dirp;
struct dirent *dp;
(void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n",
(u_long) statp->st_mtime,
(u_long) statp->st_atime);
- (void) atomicio(write, remout, path, strlen(path));
+ (void) atomicio(vwrite, remout, path, strlen(path));
if (response() < 0) {
closedir(dirp);
return;
(u_int) (statp->st_mode & FILEMODEMASK), 0, last);
if (verbose_mode)
fprintf(stderr, "Entering directory: %s", path);
- (void) atomicio(write, remout, path, strlen(path));
+ (void) atomicio(vwrite, remout, path, strlen(path));
if (response() < 0) {
closedir(dirp);
return;
source(1, vect);
}
(void) closedir(dirp);
- (void) atomicio(write, remout, "E\n", 2);
+ (void) atomicio(vwrite, remout, "E\n", 2);
(void) response();
}
void
-sink(argc, argv)
- int argc;
- char *argv[];
+bwlimit(int amount)
+{
+ static struct timeval bwstart, bwend;
+ static int lamt, thresh = 16384;
+ u_int64_t waitlen;
+ struct timespec ts, rm;
+
+ if (!timerisset(&bwstart)) {
+ gettimeofday(&bwstart, NULL);
+ return;
+ }
+
+ lamt += amount;
+ if (lamt < thresh)
+ return;
+
+ gettimeofday(&bwend, NULL);
+ timersub(&bwend, &bwstart, &bwend);
+ if (!timerisset(&bwend))
+ return;
+
+ lamt *= 8;
+ waitlen = (double)1000000L * lamt / limit_rate;
+
+ bwstart.tv_sec = waitlen / 1000000L;
+ bwstart.tv_usec = waitlen % 1000000L;
+
+ if (timercmp(&bwstart, &bwend, >)) {
+ timersub(&bwstart, &bwend, &bwend);
+
+ /* Adjust the wait time */
+ if (bwend.tv_sec) {
+ thresh /= 2;
+ if (thresh < 2048)
+ thresh = 2048;
+ } else if (bwend.tv_usec < 100) {
+ thresh *= 2;
+ if (thresh > 32768)
+ thresh = 32768;
+ }
+
+ TIMEVAL_TO_TIMESPEC(&bwend, &ts);
+ while (nanosleep(&ts, &rm) == -1) {
+ if (errno != EINTR)
+ break;
+ ts = rm;
+ }
+ }
+
+ lamt = 0;
+ gettimeofday(&bwstart, NULL);
+}
+
+void
+sink(int argc, char **argv)
{
static BUF buffer;
struct stat stb;
YES, NO, DISPLAYED
} wrerr;
BUF *bp;
- off_t i, j;
- int amt, count, exists, first, mask, mode, ofd, omode;
- off_t size;
+ off_t i;
+ size_t j, count;
+ int amt, exists, first, mask, mode, ofd, omode;
+ off_t size, statbytes;
int setimes, targisdir, wrerrno = 0;
- char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+ char ch, *cp, *np, *targ, *why, *vect[1], buf[16384];
struct timeval tv[2];
#define atime tv[0]
#define mtime tv[1]
-#define SCREWUP(str) do { why = str; goto screwup; } while (0)
+#define SCREWUP(str) { why = str; goto screwup; }
setimes = targisdir = 0;
mask = umask(0);
if (targetshouldbedirectory)
verifydir(targ);
- (void) atomicio(write, remout, "", 1);
+ (void) atomicio(vwrite, remout, "", 1);
if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
targisdir = 1;
for (first = 1;; first = 0) {
cp = buf;
- if (atomicio(read, remin, cp, 1) <= 0)
+ if (atomicio(read, remin, cp, 1) != 1)
return;
if (*cp++ == '\n')
SCREWUP("unexpected <newline>");
*cp++ = ch;
} while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
*cp = 0;
+ if (verbose_mode)
+ fprintf(stderr, "Sink: %s", buf);
if (buf[0] == '\01' || buf[0] == '\02') {
if (iamremote == 0)
- (void) atomicio(write, STDERR_FILENO,
+ (void) atomicio(vwrite, STDERR_FILENO,
buf + 1, strlen(buf + 1));
if (buf[0] == '\02')
exit(1);
continue;
}
if (buf[0] == 'E') {
- (void) atomicio(write, remout, "", 1);
+ (void) atomicio(vwrite, remout, "", 1);
return;
}
if (ch == '\n')
atime.tv_usec = strtol(cp, &cp, 10);
if (!cp || *cp++ != '\0')
SCREWUP("atime.usec not delimited");
- (void) atomicio(write, remout, "", 1);
+ (void) atomicio(vwrite, remout, "", 1);
continue;
}
if (*cp != 'C' && *cp != 'D') {
size = size * 10 + (*cp++ - '0');
if (*cp++ != ' ')
SCREWUP("size not delimited");
+ if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
+ run_err("error: unexpected filename: %s", cp);
+ exit(1);
+ }
if (targisdir) {
static char *namebuf;
- static int cursize;
+ static size_t cursize;
size_t need;
need = strlen(targ) + strlen(cp) + 250;
cursize = need;
}
(void) snprintf(namebuf, need, "%s%s%s", targ,
- *targ ? "/" : "", cp);
+ strcmp(targ, "/") ? "/" : "", cp);
np = namebuf;
} else
np = targ;
exists = stat(np, &stb) == 0;
if (buf[0] == 'D') {
int mod_flag = pflag;
+ if (!iamrecursive)
+ SCREWUP("received directory without -r");
if (exists) {
if (!S_ISDIR(stb.st_mode)) {
errno = ENOTDIR;
bad: run_err("%s: %s", np, strerror(errno));
continue;
}
- (void) atomicio(write, remout, "", 1);
- if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
+ (void) atomicio(vwrite, remout, "", 1);
+ if ((bp = allocbuf(&buffer, ofd, sizeof(buf))) == NULL) {
(void) close(ofd);
continue;
}
cp = bp->buf;
wrerr = NO;
- if (showprogress) {
- totalbytes = size;
- progressmeter(-1);
- }
statbytes = 0;
- for (count = i = 0; i < size; i += 4096) {
- amt = 4096;
+ if (showprogress)
+ start_progress_meter(curfile, size, &statbytes);
+ for (count = i = 0; i < size; i += sizeof(buf)) {
+ amt = sizeof(buf);
if (i + amt > size)
amt = size - i;
count += amt;
do {
- j = read(remin, cp, amt);
- if (j == -1 && (errno == EINTR ||
- errno == EAGAIN)) {
- continue;
- } else if (j <= 0) {
+ j = atomicio(read, remin, cp, amt);
+ if (j == 0) {
run_err("%s", j ? strerror(errno) :
"dropped connection");
exit(1);
cp += j;
statbytes += j;
} while (amt > 0);
+
+ if (limit_rate)
+ bwlimit(sizeof(buf));
+
if (count == bp->cnt) {
/* Keep reading so we stay sync'd up. */
if (wrerr == NO) {
- j = atomicio(write, ofd, bp->buf, count);
- if (j != count) {
+ if (atomicio(vwrite, ofd, bp->buf,
+ count) != count) {
wrerr = YES;
- wrerrno = j >= 0 ? EIO : errno;
+ wrerrno = errno;
}
}
count = 0;
}
}
if (showprogress)
- progressmeter(1);
+ stop_progress_meter();
if (count != 0 && wrerr == NO &&
- (j = atomicio(write, ofd, bp->buf, count)) != count) {
+ atomicio(vwrite, ofd, bp->buf, count) != count) {
wrerr = YES;
- wrerrno = j >= 0 ? EIO : errno;
+ wrerrno = errno;
}
- if (ftruncate(ofd, size)) {
+ if (wrerr == NO && ftruncate(ofd, size) != 0) {
run_err("%s: truncate: %s", np, strerror(errno));
wrerr = DISPLAYED;
}
if (pflag) {
if (exists || omode != mode)
#ifdef HAVE_FCHMOD
- if (fchmod(ofd, omode))
+ if (fchmod(ofd, omode)) {
#else /* HAVE_FCHMOD */
- if (chmod(np, omode))
+ if (chmod(np, omode)) {
#endif /* HAVE_FCHMOD */
run_err("%s: set mode: %s",
np, strerror(errno));
+ wrerr = DISPLAYED;
+ }
} else {
if (!exists && omode != mode)
#ifdef HAVE_FCHMOD
- if (fchmod(ofd, omode & ~mask))
+ if (fchmod(ofd, omode & ~mask)) {
#else /* HAVE_FCHMOD */
- if (chmod(np, omode & ~mask))
+ if (chmod(np, omode & ~mask)) {
#endif /* HAVE_FCHMOD */
run_err("%s: set mode: %s",
np, strerror(errno));
+ wrerr = DISPLAYED;
+ }
}
if (close(ofd) == -1) {
wrerr = YES;
run_err("%s: %s", np, strerror(wrerrno));
break;
case NO:
- (void) atomicio(write, remout, "", 1);
+ (void) atomicio(vwrite, remout, "", 1);
break;
case DISPLAYED:
break;
}
int
-response()
+response(void)
{
char ch, *cp, resp, rbuf[2048];
} while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
if (!iamremote)
- (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf);
+ (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf);
++errs;
if (resp == 1)
return (-1);
}
void
-usage()
+usage(void)
{
(void) fprintf(stderr,
- "usage: scp [-pqrvBC46] [-F config] [-S ssh] [-P port] [-c cipher] [-i identity]\n"
- " [-o option] f1 f2\n"
- " or: scp [options] f1 ... fn directory\n");
+ "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
+ " [-l limit] [-o ssh_option] [-P port] [-w buffer size] [-S program]\n"
+ " [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
exit(1);
}
}
void
-verifydir(cp)
- char *cp;
+verifydir(char *cp)
{
struct stat stb;
errno = ENOTDIR;
}
run_err("%s: %s", cp, strerror(errno));
- exit(1);
+ killchild(0);
}
int
-okname(cp0)
- char *cp0;
+okname(char *cp0)
{
int c;
char *cp;
c = (int)*cp;
if (c & 0200)
goto bad;
- if (!isalpha(c) && !isdigit(c) &&
- c != '_' && c != '-' && c != '.' && c != '+')
- goto bad;
+ if (!isalpha(c) && !isdigit(c)) {
+ switch (c) {
+ case '\'':
+ case '"':
+ case '`':
+ case ' ':
+ case '#':
+ goto bad;
+ default:
+ break;
+ }
+ }
} while (*++cp);
return (1);
}
BUF *
-allocbuf(bp, fd, blksize)
- BUF *bp;
- int fd, blksize;
+allocbuf(BUF *bp, int fd, int blksize)
{
size_t size;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
run_err("fstat: %s", strerror(errno));
return (0);
}
- if (stb.st_blksize == 0)
+ size = roundup(stb.st_blksize, blksize);
+ if (size == 0)
size = blksize;
- else
- size = blksize + (stb.st_blksize - blksize % stb.st_blksize) %
- stb.st_blksize;
#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
size = blksize;
#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
}
void
-lostconn(signo)
- int signo;
+lostconn(int signo)
{
if (!iamremote)
write(STDERR_FILENO, "lost connection\n", 16);
else
exit(1);
}
-
-static void
-updateprogressmeter(int ignore)
-{
- int save_errno = errno;
-
- progressmeter(0);
- signal(SIGALRM, updateprogressmeter);
- alarm(PROGRESSTIME);
- errno = save_errno;
-}
-
-static int
-foregroundproc(void)
-{
- static pid_t pgrp = -1;
- int ctty_pgrp;
-
- if (pgrp == -1)
- pgrp = getpgrp();
-
-#ifdef HAVE_TCGETPGRP
- return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 &&
- ctty_pgrp == pgrp);
-#else
- return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
- ctty_pgrp == pgrp));
-#endif
-}
-
-void
-progressmeter(int flag)
-{
- static const char prefixes[] = " KMGTP";
- static struct timeval lastupdate;
- static off_t lastsize;
- struct timeval now, td, wait;
- off_t cursize, abbrevsize;
- double elapsed;
- int ratio, barlength, i, remaining;
- char buf[256];
-
- if (flag == -1) {
- (void) gettimeofday(&start, (struct timezone *) 0);
- lastupdate = start;
- lastsize = 0;
- }
- if (foregroundproc() == 0)
- return;
-
- (void) gettimeofday(&now, (struct timezone *) 0);
- cursize = statbytes;
- if (totalbytes != 0) {
- ratio = 100.0 * cursize / totalbytes;
- ratio = MAX(ratio, 0);
- ratio = MIN(ratio, 100);
- } else
- ratio = 100;
-
- snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio);
-
- barlength = getttywidth() - 51;
- barlength = (barlength <= MAX_BARLENGTH)?barlength:MAX_BARLENGTH;
- if (barlength > 0) {
- i = barlength * ratio / 100;
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "|%.*s%*s|", i, BAR, barlength - i, "");
- }
- i = 0;
- abbrevsize = cursize;
- while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
- i++;
- abbrevsize >>= 10;
- }
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5lu %c%c ",
- (unsigned long) abbrevsize, prefixes[i],
- prefixes[i] == ' ' ? ' ' : 'B');
-
- timersub(&now, &lastupdate, &wait);
- if (cursize > lastsize) {
- lastupdate = now;
- lastsize = cursize;
- if (wait.tv_sec >= STALLTIME) {
- start.tv_sec += wait.tv_sec;
- start.tv_usec += wait.tv_usec;
- }
- wait.tv_sec = 0;
- }
- timersub(&now, &start, &td);
- elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
-
- if (flag != 1 &&
- (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) {
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- " --:-- ETA");
- } else if (wait.tv_sec >= STALLTIME) {
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- " - stalled -");
- } else {
- if (flag != 1)
- remaining = (int)(totalbytes / (statbytes / elapsed) -
- elapsed);
- else
- remaining = elapsed;
-
- i = remaining / 3600;
- if (i)
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "%2d:", i);
- else
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- " ");
- i = remaining % 3600;
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
- "%02d:%02d%s", i / 60, i % 60,
- (flag != 1) ? " ETA" : " ");
- }
- atomicio(write, fileno(stdout), buf, strlen(buf));
-
- if (flag == -1) {
- mysignal(SIGALRM, updateprogressmeter);
- alarm(PROGRESSTIME);
- } else if (flag == 1) {
- alarm(0);
- atomicio(write, fileno(stdout), "\n", 1);
- statbytes = 0;
- }
-}
-
-int
-getttywidth(void)
-{
- struct winsize winsize;
-
- if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
- return (winsize.ws_col ? winsize.ws_col : 80);
- else
- return (80);
-}
*/
#include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.83 2001/11/09 18:59:23 markus Exp $");
+RCSID("$OpenBSD: serverloop.c,v 1.118 2005/07/17 07:17:55 djm Exp $");
#include "xmalloc.h"
#include "packet.h"
#include "buffer.h"
#include "log.h"
#include "servconf.h"
+#include "canohost.h"
#include "sshpty.h"
#include "channels.h"
#include "compat.h"
/* XXX */
extern Kex *xxx_kex;
-static Authctxt *xxx_authctxt;
+extern Authctxt *the_authctxt;
static Buffer stdin_buffer; /* Buffer for stdin data. */
static Buffer stdout_buffer; /* Buffer for stdout data. */
* will exit after that, as soon as forwarded connections have terminated.
*/
-static volatile int child_terminated; /* The child has terminated. */
+static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */
/* prototypes */
static void server_init_dispatch(void);
+/*
+ * we write to this pipe if a SIGCHLD is caught in order to avoid
+ * the race between select() and child_terminated
+ */
+static int notify_pipe[2];
+static void
+notify_setup(void)
+{
+ if (pipe(notify_pipe) < 0) {
+ error("pipe(notify_pipe) failed %s", strerror(errno));
+ } else if ((fcntl(notify_pipe[0], F_SETFD, 1) == -1) ||
+ (fcntl(notify_pipe[1], F_SETFD, 1) == -1)) {
+ error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno));
+ close(notify_pipe[0]);
+ close(notify_pipe[1]);
+ } else {
+ set_nonblock(notify_pipe[0]);
+ set_nonblock(notify_pipe[1]);
+ return;
+ }
+ notify_pipe[0] = -1; /* read end */
+ notify_pipe[1] = -1; /* write end */
+}
+static void
+notify_parent(void)
+{
+ if (notify_pipe[1] != -1)
+ write(notify_pipe[1], "", 1);
+}
+static void
+notify_prepare(fd_set *readset)
+{
+ if (notify_pipe[0] != -1)
+ FD_SET(notify_pipe[0], readset);
+}
+static void
+notify_done(fd_set *readset)
+{
+ char c;
+
+ if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset))
+ while (read(notify_pipe[0], &c, 1) != -1)
+ debug2("notify_done: reading");
+}
+
static void
sigchld_handler(int sig)
{
int save_errno = errno;
debug("Received SIGCHLD.");
child_terminated = 1;
+#ifndef _UNICOS
mysignal(SIGCHLD, sigchld_handler);
+#endif
+ notify_parent();
errno = save_errno;
}
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)
{
- int id;
+ 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.");
- id = channel_find_open();
- if (id == -1)
- packet_disconnect("No open channels after timeout!");
/*
- * 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;
int client_alive_scheduled = 0;
/*
- * if using client_alive, set the max timeout accordingly,
+ * if using client_alive, set the max timeout accordingly,
* and indicate that this particular timeout was for client
* alive by setting the client_alive_scheduled flag.
*
* this could be randomized somewhat to make traffic
- * analysis more difficult, but we're not doing it yet.
+ * analysis more difficult, but we're not doing it yet.
*/
if (compat20 &&
max_time_milliseconds == 0 && options.client_alive_interval) {
if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
FD_SET(fdin, *writesetp);
}
+ notify_prepare(*readsetp);
/*
* If we have buffered packet data going to the client, mark that
tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
tvp = &tv;
}
- if (tvp!=NULL)
- debug3("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
/* Wait for something to happen, or the timeout to expire. */
ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
error("select: %.100s", strerror(errno));
} else if (ret == 0 && client_alive_scheduled)
client_alive_check();
+
+ notify_done(*readsetp);
}
/*
if (FD_ISSET(connection_in, readset)) {
len = read(connection_in, buf, sizeof(buf));
if (len == 0) {
- verbose("Connection closed by remote host.");
+ verbose("Connection closed by %.100s",
+ get_remote_ipaddr());
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", strerror(errno));
- fatal_cleanup();
+ verbose("Read error from remote host "
+ "%.100s: %.100s",
+ get_remote_ipaddr(), strerror(errno));
+ cleanup_exit(255);
}
} else {
/* Buffer any received data. */
if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
/* do nothing */
} else if (len <= 0) {
-#ifdef USE_PIPES
- close(fdin);
-#else
if (fdin != fdout)
close(fdin);
else
shutdown(fdin, SHUT_WR); /* We will no longer send. */
-#endif
fdin = -1;
} else {
/* Successful write. */
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. */
connection_in = packet_get_connection_in();
connection_out = packet_get_connection_out();
+ notify_setup();
+
previous_stdout_buffer_bytes = 0;
/* Set approximate I/O buffer size. */
* input data, cause a real eof by closing fdin.
*/
if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) {
-#ifdef USE_PIPES
- close(fdin);
-#else
if (fdin != fdout)
close(fdin);
else
shutdown(fdin, SHUT_WR); /* We will no longer send. */
-#endif
fdin = -1;
}
/* Make packets from buffered stderr data to send to the client. */
max_fd = MAX(max_fd, fdin);
max_fd = MAX(max_fd, fdout);
max_fd = MAX(max_fd, fderr);
+ max_fd = MAX(max_fd, notify_pipe[0]);
/* Sleep in select() until we can do something. */
wait_until_can_do_something(&readset, &writeset, &max_fd,
drain_output();
debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
- stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
+ stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
/* Free and clear the buffers. */
buffer_free(&stdin_buffer);
/* We no longer want our SIGCHLD handler to be called. */
mysignal(SIGCHLD, SIG_DFL);
- wait_pid = waitpid(-1, &wait_status, child_terminated ? WNOHANG : 0);
- if (wait_pid == -1)
- packet_disconnect("wait: %.100s", strerror(errno));
- else if (wait_pid != pid)
- error("Strange, wait returned pid %d, expected %d",
- wait_pid, pid);
+ while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0)
+ if (errno != EINTR)
+ packet_disconnect("wait: %.100s", strerror(errno));
+ if (wait_pid != pid)
+ error("Strange, wait returned pid %ld, expected %ld",
+ (long)wait_pid, (long)pid);
/* Check if it exited normally. */
if (WIFEXITED(wait_status)) {
* the exit status.
*/
do {
- int plen;
- type = packet_read(&plen);
+ type = packet_read();
}
while (type != SSH_CMSG_EXIT_CONFIRMATION);
sigaddset(&nset, SIGCHLD);
sigprocmask(SIG_BLOCK, &nset, &oset);
if (child_terminated) {
- while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
- session_close_by_pid(pid, status);
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
+ (pid < 0 && errno == EINTR))
+ if (pid > 0)
+ session_close_by_pid(pid, status);
child_terminated = 0;
}
sigprocmask(SIG_SETMASK, &oset, NULL);
connection_in = packet_get_connection_in();
connection_out = packet_get_connection_out();
+ notify_setup();
+
max_fd = MAX(connection_in, connection_out);
- xxx_authctxt = authctxt;
+ max_fd = MAX(max_fd, notify_pipe[0]);
server_init_dispatch();
&nalloc, 0);
collect_children();
- if (!rekeying)
+ if (!rekeying) {
channel_after_select(readset, writeset);
+ if (packet_need_rekeying()) {
+ debug("need rekeying");
+ xxx_kex->done = 0;
+ kex_send_kexinit(xxx_kex);
+ }
+ }
process_input(readset);
if (connection_closed)
break;
channel_free_all();
/* free remaining sessions, e.g. remove wtmp entries */
- session_destroy_all();
+ session_destroy_all(NULL);
}
static void
-server_input_channel_failure(int type, int plen, 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
* the bogus CHANNEL_REQUEST we send for keepalives.
*/
- client_alive_timeouts = 0;
+ client_alive_timeouts = 0;
}
-
static void
-server_input_stdin_data(int type, int plen, void *ctxt)
+server_input_stdin_data(int type, u_int32_t seq, void *ctxt)
{
char *data;
u_int data_len;
if (fdin == -1)
return;
data = packet_get_string(&data_len);
- packet_integrity_check(plen, (4 + data_len), type);
+ packet_check_eom();
buffer_append(&stdin_buffer, data, data_len);
memset(data, 0, data_len);
xfree(data);
}
static void
-server_input_eof(int type, int plen, void *ctxt)
+server_input_eof(int type, u_int32_t seq, void *ctxt)
{
/*
* Eof from the client. The stdin descriptor to the
* drained.
*/
debug("EOF received for stdin.");
- packet_integrity_check(plen, 0, type);
+ packet_check_eom();
stdin_eof = 1;
}
static void
-server_input_window_size(int type, int plen, void *ctxt)
+server_input_window_size(int type, u_int32_t seq, void *ctxt)
{
int row = packet_get_int();
int col = packet_get_int();
int ypixel = packet_get_int();
debug("Window change received.");
- packet_integrity_check(plen, 4 * 4, type);
+ packet_check_eom();
if (fdin != -1)
pty_change_window_size(fdin, row, col, xpixel, ypixel);
}
static Channel *
-server_request_direct_tcpip(char *ctype)
+server_request_direct_tcpip(void)
{
Channel *c;
int sock;
target_port = packet_get_int();
originator = packet_get_string(NULL);
originator_port = packet_get_int();
- packet_done();
+ 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, xstrdup("direct-tcpip"), 1);
- if (c == NULL) {
- error("server_request_direct_tcpip: channel_new failed");
- close(sock);
- }
+ CHAN_TCP_PACKET_DEFAULT, 0, "direct-tcpip", 1);
return c;
}
static Channel *
-server_request_session(char *ctype)
+server_request_session(void)
{
Channel *c;
debug("input_session_request");
- packet_done();
+ packet_check_eom();
/*
* 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.
*/
- 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, xstrdup("server-session"), 1);
- if (c == NULL) {
- error("server_request_session: channel_new failed");
- return NULL;
- }
- if (session_open(xxx_authctxt, c->self) != 1) {
+ 0, "server-session", 1);
+ if (!(datafellows & SSH_BUG_LARGEWINDOW))
+ c->dynamic_window = 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_callback(c->self, SSH2_MSG_CHANNEL_REQUEST,
- session_input_channel_req, (void *)0);
channel_register_cleanup(c->self, session_close_by_channel);
return c;
}
static void
-server_input_channel_open(int type, int plen, void *ctxt)
+server_input_channel_open(int type, u_int32_t seq, void *ctxt)
{
Channel *c = NULL;
char *ctype;
- u_int len;
int rchan;
- int rmaxpack;
- int rwindow;
+ u_int rmaxpack, rwindow, len;
ctype = packet_get_string(&len);
rchan = packet_get_int();
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();
}
if (c != NULL) {
debug("server_input_channel_open: confirm %s", ctype);
}
static void
-server_input_global_request(int type, int plen, void *ctxt)
+server_input_global_request(int type, u_int32_t seq, void *ctxt)
{
char *rtype;
int want_reply;
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);
/* check permissions */
if (!options.allow_tcp_forwarding ||
- no_port_forwarding_flag ||
- (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)) {
+ no_port_forwarding_flag
+#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 {
/* Start listening on the port */
- success = channel_request_forwarding(
- listen_address, listen_port,
- /*unspec host_to_connect*/ "<unspec host>",
- /*unspec port_to_connect*/ 0,
- options.gateway_ports, /*remote*/ 1);
+ success = channel_setup_remote_fwd_listener(
+ 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 ?
}
xfree(rtype);
}
+static void
+server_input_channel_req(int type, u_int32_t seq, void *ctxt)
+{
+ Channel *c;
+ int id, reply, success = 0;
+ char *rtype;
+
+ id = packet_get_int();
+ rtype = packet_get_string(NULL);
+ reply = packet_get_char();
+
+ debug("server_input_channel_req: channel %d request %s reply %d",
+ id, rtype, reply);
+
+ if ((c = channel_lookup(id)) == NULL)
+ packet_disconnect("server_input_channel_req: "
+ "unknown channel %d", id);
+ if (c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN)
+ success = session_input_channel_req(c, rtype);
+ if (reply) {
+ packet_start(success ?
+ SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
+ packet_put_int(c->remote_id);
+ packet_send();
+ }
+ xfree(rtype);
+}
static void
server_init_dispatch_20(void)
dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open);
dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
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_REQUEST, &server_input_channel_req);
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);
}
{
fprintf(stderr,
"usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
-" [-D port] [-e escape_char] [-F configfile]\n"
+" [-D port] [-e escape_char] [-F configfile] [-w receive buffer size]\n"
" [-i identity_file] [-L [bind_address:]port:host:hostport]\n"
" [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
" [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
/* Parse command-line arguments. */
host = NULL;
+ /* need to set options.tcp_rcv_buf to 0 */
+ options.tcp_rcv_buf = 0;
+
again:
while ((opt = getopt(ac, av,
- "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVXY")) != -1) {
+ "1246ab:c:e:fgi:kl:m:no:p:qstvw:xzACD:F:I:L:MNO:PR:S:TVXY")) != -1) {
switch (opt) {
case '1':
options.protocol = SSH_PROTO_1;
break;
case 'T':
no_tty_flag = 1;
+ options.none_switch = 0;
break;
case 'o':
dummy = 1;
case 'F':
config = optarg;
break;
+ case 'w':
+ options.tcp_rcv_buf = atoi(optarg);
+ break;
+ case 'z':
+ /* make sure we can't turn on the none_switch */
+ /* if they try to force a no tty flag on a tty session */
+ if (!no_tty_flag) {
+ options.none_switch = 1;
+ }
+ break;
+
default:
usage();
}
window = CHAN_SES_WINDOW_DEFAULT;
packetmax = CHAN_SES_PACKET_DEFAULT;
if (tty_flag) {
+ window = 4*CHAN_SES_PACKET_DEFAULT;
window >>= 1;
packetmax >>= 1;
}
"session", SSH_CHANNEL_OPENING, in, out, err,
window, packetmax, CHAN_EXTENDED_WRITE,
"client-session", /*nonblock*/0);
-
+ if (!tty_flag && (!(datafellows & SSH_BUG_LARGEWINDOW))) {
+ c->dynamic_window = 1;
+ }
debug3("ssh_session2_open: channel_new: %d", c->self);
channel_send_open(c->self);
*/
#include "includes.h"
-RCSID("$OpenBSD: sshconnect.c,v 1.115 2001/10/08 19:05:05 markus Exp $");
+RCSID("$OpenBSD: sshconnect.c,v 1.168 2005/07/17 07:17:55 djm Exp $");
#include <openssl/bn.h>
#include "atomicio.h"
#include "misc.h"
+#include "dns.h"
+
char *client_version_string = NULL;
char *server_version_string = NULL;
+int matching_host_key_dns = 0;
+
+/* import */
extern Options options;
extern char *__progname;
+extern uid_t original_real_uid;
+extern uid_t original_effective_uid;
+extern pid_t proxy_command_pid;
#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */
#define INET6_ADDRSTRLEN 46
#endif
-static const char *
-sockaddr_ntop(struct sockaddr *sa)
-{
- void *addr;
- static char addrbuf[INET6_ADDRSTRLEN];
-
- switch (sa->sa_family) {
- case AF_INET:
- addr = &((struct sockaddr_in *)sa)->sin_addr;
- break;
- case AF_INET6:
- addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
- break;
- default:
- /* This case should be protected against elsewhere */
- abort();
- }
- inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
- return addrbuf;
-}
+static int show_other_keys(const char *, Key *);
+static void warn_changed_key(Key *);
/*
* Connect to the given ssh server using a proxy command.
*/
static int
-ssh_proxy_connect(const char *host, u_short port, struct passwd *pw,
- const char *proxy_command)
+ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
{
- Buffer command;
- const char *cp;
- char *command_string;
+ char *command_string, *tmp;
int pin[2], pout[2];
pid_t pid;
char strport[NI_MAXSERV];
+ size_t len;
/* Convert the port number into a string. */
snprintf(strport, sizeof strport, "%hu", port);
- /* Build the final command string in the buffer by making the
- appropriate substitutions to the given proxy command. */
- buffer_init(&command);
- for (cp = proxy_command; *cp; cp++) {
- if (cp[0] == '%' && cp[1] == '%') {
- buffer_append(&command, "%", 1);
- cp++;
- continue;
- }
- if (cp[0] == '%' && cp[1] == 'h') {
- buffer_append(&command, host, strlen(host));
- cp++;
- continue;
- }
- if (cp[0] == '%' && cp[1] == 'p') {
- buffer_append(&command, strport, strlen(strport));
- cp++;
- continue;
- }
- buffer_append(&command, cp, 1);
- }
- buffer_append(&command, "\0", 1);
-
- /* Get the final command string. */
- command_string = buffer_ptr(&command);
+ /*
+ * Build the final command string in the buffer by making the
+ * appropriate substitutions to the given proxy command.
+ *
+ * Use "exec" to avoid "sh -c" processes on some platforms
+ * (e.g. Solaris)
+ */
+ len = strlen(proxy_command) + 6;
+ tmp = xmalloc(len);
+ strlcpy(tmp, "exec ", len);
+ strlcat(tmp, proxy_command, len);
+ command_string = percent_expand(tmp, "h", host,
+ "p", strport, (char *)NULL);
+ xfree(tmp);
/* Create pipes for communicating with the proxy. */
if (pipe(pin) < 0 || pipe(pout) < 0)
fatal("Could not create pipes to communicate with the proxy: %.100s",
- strerror(errno));
+ strerror(errno));
debug("Executing proxy command: %.500s", command_string);
char *argv[10];
/* Child. Permanently give up superuser privileges. */
- permanently_set_uid(pw);
+ seteuid(original_real_uid);
+ setuid(original_real_uid);
/* Redirect stdin and stdout. */
close(pin[1]);
/* Parent. */
if (pid < 0)
fatal("fork failed: %.100s", strerror(errno));
+ else
+ proxy_command_pid = pid; /* save pid to clean up later */
/* Close child side of the descriptors. */
close(pin[0]);
close(pout[1]);
/* Free the command name. */
- buffer_free(&command);
+ xfree(command_string);
/* Set the connection file descriptors. */
packet_set_connection(pout[0], pin[1]);
* Creates a (possibly privileged) socket for use as the ssh connection.
*/
static int
-ssh_create_socket(struct passwd *pw, int privileged, int family)
+ssh_create_socket(int privileged, struct addrinfo *ai)
{
int sock, gaierr;
struct addrinfo hints, *res;
*/
if (privileged) {
int p = IPPORT_RESERVED - 1;
- sock = rresvport_af(&p, family);
+ PRIV_START;
+ sock = rresvport_af(&p, ai->ai_family);
+ PRIV_END;
if (sock < 0)
- error("rresvport: af=%d %.100s", family, strerror(errno));
+ error("rresvport: af=%d %.100s", ai->ai_family,
+ strerror(errno));
else
debug("Allocated local port %d.", p);
+
+
+ /* tuning needs to happen after the socket is */
+ /* created but before the connection happens */
+ /* so winscale is negotiated properly -cjr */
+
+ /* Set tcp receive buffer if requested */
+ if (options.tcp_rcv_buf)
+ {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ (void *)&options.tcp_rcv_buf,
+ sizeof(options.tcp_rcv_buf)) >= 0)
+ {
+ debug("setsockopt SO_RCVBUF: %.100s", strerror(errno));
+ }
+ else
+ {
+ /* coudln't set the socket size to use spec. */
+ /* should default to system param and continue */
+ /* warn the user though - cjr */
+ error("Couldn't set socket receive buffer as requested. Continuing anyway.");
+ }
+ }
return sock;
}
- /*
- * Just create an ordinary socket on arbitrary port. We use
- * the user's uid to create the socket.
- */
- temporarily_use_uid(pw);
- sock = socket(family, SOCK_STREAM, 0);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0)
error("socket: %.100s", strerror(errno));
- restore_uid();
-
- /* Bind the socket to an alternative local IP address */
+
+ /* tuning needs to happen after the socket is */
+ /* created but before the connection happens */
+ /* so winscale is negotiated properly -cjr */
+
+ /* Set tcp receive buffer if requested */
+ if (options.tcp_rcv_buf)
+ {
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ (void *)&options.tcp_rcv_buf,
+ sizeof(options.tcp_rcv_buf)) >= 0)
+ {
+ debug("setsockopt SO_RCVBUF: %.100s", strerror(errno));
+ }
+ else
+ {
+ /* coudln't set the socket size to use spec. */
+ /* should default to system param and continue */
+ /* warn the user though - cjr */
+ error("Couldn't set socket receive buffer as requested. Continuing anyway.");
+ }
+ }
+
+ /* Bind the socket to an alternative local IP address */
if (options.bind_address == NULL)
return sock;
memset(&hints, 0, sizeof(hints));
- hints.ai_family = family;
- hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = ai->ai_family;
+ hints.ai_socktype = ai->ai_socktype;
+ hints.ai_protocol = ai->ai_protocol;
hints.ai_flags = AI_PASSIVE;
gaierr = getaddrinfo(options.bind_address, "0", &hints, &res);
if (gaierr) {
return sock;
}
+static int
+timeout_connect(int sockfd, const struct sockaddr *serv_addr,
+ socklen_t addrlen, int timeout)
+{
+ fd_set *fdset;
+ struct timeval tv;
+ socklen_t optlen;
+ int fdsetsz, optval, rc, result = -1;
+
+ if (timeout <= 0)
+ return (connect(sockfd, serv_addr, addrlen));
+
+ set_nonblock(sockfd);
+ rc = connect(sockfd, serv_addr, addrlen);
+ if (rc == 0) {
+ unset_nonblock(sockfd);
+ return (0);
+ }
+ if (errno != EINPROGRESS)
+ return (-1);
+
+ fdsetsz = howmany(sockfd + 1, NFDBITS) * sizeof(fd_mask);
+ fdset = (fd_set *)xmalloc(fdsetsz);
+
+ memset(fdset, 0, fdsetsz);
+ FD_SET(sockfd, fdset);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ for (;;) {
+ rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
+ if (rc != -1 || errno != EINTR)
+ break;
+ }
+
+ switch (rc) {
+ case 0:
+ /* Timed out */
+ errno = ETIMEDOUT;
+ break;
+ case -1:
+ /* Select error */
+ debug("select: %s", strerror(errno));
+ break;
+ case 1:
+ /* Completed or failed */
+ optval = 0;
+ optlen = sizeof(optval);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval,
+ &optlen) == -1) {
+ debug("getsockopt: %s", strerror(errno));
+ break;
+ }
+ if (optval != 0) {
+ errno = optval;
+ break;
+ }
+ result = 0;
+ unset_nonblock(sockfd);
+ break;
+ default:
+ /* Should not occur */
+ fatal("Bogus return (%d) from select()", rc);
+ }
+
+ xfree(fdset);
+ return (result);
+}
+
/*
* Opens a TCP/IP connection to the remote server on the given host.
* The address of the remote host will be returned in hostaddr.
- * If port is 0, the default port will be used. If anonymous is zero,
+ * If port is 0, the default port will be used. If needpriv is true,
* a privileged port will be allocated to make the connection.
- * This requires super-user privileges if anonymous is false.
+ * This requires super-user privileges if needpriv is true.
* Connection_attempts specifies the maximum number of tries (one per
* second). If proxy_command is non-NULL, it specifies the command (with %h
* and %p substituted for host and port, respectively) to use to contact
* the daemon.
- * Return values:
- * 0 for OK
- * ECONNREFUSED if we got a "Connection Refused" by the peer on any address
- * ECONNABORTED if we failed without a "Connection refused"
- * Suitable error messages for the connection failure will already have been
- * printed.
*/
int
ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
u_short port, int family, int connection_attempts,
- int anonymous, struct passwd *pw, const char *proxy_command)
+ int needpriv, const char *proxy_command)
{
int gaierr;
int on = 1;
int sock = -1, attempt;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
struct addrinfo hints, *ai, *aitop;
- struct linger linger;
- struct servent *sp;
- /*
- * Did we get only other errors than "Connection refused" (which
- * should block fallback to rsh and similar), or did we get at least
- * one "Connection refused"?
- */
- int full_failure = 1;
- debug("ssh_connect: getuid %u geteuid %u anon %d",
- (u_int) getuid(), (u_int) geteuid(), anonymous);
+ debug2("ssh_connect: needpriv %d", needpriv);
- /* Get default port if port has not been set. */
- if (port == 0) {
- sp = getservbyname(SSH_SERVICE_NAME, "tcp");
- if (sp)
- port = ntohs(sp->s_port);
- else
- port = SSH_DEFAULT_PORT;
- }
/* If a proxy command is given, connect using it. */
if (proxy_command != NULL)
- return ssh_proxy_connect(host, port, pw, proxy_command);
+ return ssh_proxy_connect(host, port, proxy_command);
/* No proxy command. */
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
- snprintf(strport, sizeof strport, "%d", port);
+ snprintf(strport, sizeof strport, "%u", port);
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
fatal("%s: %.100s: %s", __progname, host,
gai_strerror(gaierr));
host, ntop, strport);
/* Create a socket for connecting. */
- sock = ssh_create_socket(pw,
-#ifdef HAVE_CYGWIN
- !anonymous,
-#else
- !anonymous && geteuid() == 0,
-#endif
- ai->ai_family);
+ sock = ssh_create_socket(needpriv, ai);
if (sock < 0)
/* Any error is already output */
continue;
- /* Connect to the host. We use the user's uid in the
- * hope that it will help with tcp_wrappers showing
- * the remote uid as root.
- */
- temporarily_use_uid(pw);
- if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
+ if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
+ options.connection_timeout) >= 0) {
/* Successful connection. */
memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
- restore_uid();
break;
} else {
- if (errno == ECONNREFUSED)
- full_failure = 0;
- log("ssh: connect to address %s port %s: %s",
- sockaddr_ntop(ai->ai_addr), strport,
- strerror(errno));
- restore_uid();
+ debug("connect to address %s port %s: %s",
+ ntop, strport, strerror(errno));
/*
* Close the failed socket; there appear to
* be some problems when reusing a socket for
* which connect() has already returned an
* error.
*/
- shutdown(sock, SHUT_RDWR);
close(sock);
}
}
freeaddrinfo(aitop);
/* Return failure if we didn't get a successful connection. */
- if (attempt >= connection_attempts)
- return full_failure ? ECONNABORTED : ECONNREFUSED;
+ if (attempt >= connection_attempts) {
+ error("ssh: connect to host %s port %s: %s",
+ host, strport, strerror(errno));
+ return (-1);
+ }
debug("Connection established.");
- /*
- * Set socket options. We would like the socket to disappear as soon
- * as it has been closed for whatever reason.
- */
- /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
- linger.l_onoff = 1;
- linger.l_linger = 5;
- setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
-
- /* Set keepalives if requested. */
- if (options.keepalives &&
+ /* Set SO_KEEPALIVE if requested. */
+ if (options.tcp_keep_alive &&
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
sizeof(on)) < 0)
error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
ssh_exchange_identification(void)
{
char buf[256], remote_version[256]; /* must be same size! */
- int remote_major, remote_minor, i, mismatch;
+ int remote_major, remote_minor, mismatch;
int connection_in = packet_get_connection_in();
int connection_out = packet_get_connection_out();
int minor1 = PROTOCOL_MINOR_1;
+ u_int i;
- /* Read other side\'s version identification. */
+ /* Read other side's version identification. */
for (;;) {
for (i = 0; i < sizeof(buf) - 1; i++) {
- int len = atomicio(read, connection_in, &buf[i], 1);
- if (len < 0)
- fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
- if (len != 1)
+ size_t len = atomicio(read, connection_in, &buf[i], 1);
+
+ if (len != 1 && errno == EPIPE)
fatal("ssh_exchange_identification: Connection closed by remote host");
+ else if (len != 1)
+ fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
if (buf[i] == '\r') {
buf[i] = '\n';
buf[i + 1] = 0;
&remote_major, &remote_minor, remote_version) != 3)
fatal("Bad remote protocol version identification: '%.100s'", buf);
debug("Remote protocol version %d.%d, remote software version %.100s",
- remote_major, remote_minor, remote_version);
+ remote_major, remote_minor, remote_version);
compat_datafellows(remote_version);
mismatch = 0;
- switch(remote_major) {
+ switch (remote_major) {
case 1:
if (remote_minor == 99 &&
(options.protocol & SSH_PROTO_2) &&
enable_compat13();
minor1 = 3;
if (options.forward_agent) {
- log("Agent forwarding disabled for protocol 1.3");
+ logit("Agent forwarding disabled for protocol 1.3");
options.forward_agent = 0;
}
}
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
compat20 ? PROTOCOL_MINOR_2 : minor1,
- SSH_VERSION);
- if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
+ SSH_RELEASE);
+ if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf))
fatal("write: %.100s", strerror(errno));
client_version_string = xstrdup(buf);
chop(client_version_string);
static int
confirm(const char *prompt)
{
- char buf[1024];
- FILE *f;
- int retval = -1;
+ const char *msg, *again = "Please type 'yes' or 'no': ";
+ char *p;
+ int ret = -1;
if (options.batch_mode)
return 0;
- if (isatty(STDIN_FILENO))
- f = stdin;
- else
- f = fopen(_PATH_TTY, "rw");
- if (f == NULL)
- return 0;
- fflush(stdout);
- fprintf(stderr, "%s", prompt);
- while (1) {
- if (fgets(buf, sizeof(buf), f) == NULL) {
- fprintf(stderr, "\n");
- strlcpy(buf, "no", sizeof buf);
- }
- /* Remove newline from response. */
- if (strchr(buf, '\n'))
- *strchr(buf, '\n') = 0;
- if (strcmp(buf, "yes") == 0)
- retval = 1;
- else if (strcmp(buf, "no") == 0)
- retval = 0;
- else
- fprintf(stderr, "Please type 'yes' or 'no': ");
-
- if (retval != -1) {
- if (f != stdin)
- fclose(f);
- return retval;
- }
+ for (msg = prompt;;msg = again) {
+ p = read_passphrase(msg, RP_ECHO);
+ if (p == NULL ||
+ (p[0] == '\0') || (p[0] == '\n') ||
+ strncasecmp(p, "no", 2) == 0)
+ ret = 0;
+ if (p && strncasecmp(p, "yes", 3) == 0)
+ ret = 1;
+ if (p)
+ xfree(p);
+ if (ret != -1)
+ return ret;
}
}
* check whether the supplied host key is valid, return -1 if the key
* is not valid. the user_hostfile will not be updated if 'readonly' is true.
*/
-
static int
check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key,
int readonly, const char *user_hostfile, const char *system_hostfile)
{
Key *file_key;
- char *type = key_type(host_key);
+ const char *type = key_type(host_key);
char *ip = NULL;
char hostline[1000], *hostp, *fp;
HostStatus host_status;
HostStatus ip_status;
- int local = 0, host_ip_differ = 0;
+ int r, local = 0, host_ip_differ = 0;
int salen;
char ntop[NI_MAXHOST];
- int host_line, ip_line;
+ char msg[1024];
+ int len, host_line, ip_line;
const char *host_file = NULL, *ip_file = NULL;
/*
*/
host_file = user_hostfile;
host_status = check_host_in_hostfile(host_file, host, host_key,
- file_key, &host_line);
+ file_key, &host_line);
if (host_status == HOST_NEW) {
host_file = system_hostfile;
host_status = check_host_in_hostfile(host_file, host, host_key,
debug("Found key in %s:%d", host_file, host_line);
if (options.check_host_ip && ip_status == HOST_NEW) {
if (readonly)
- log("%s host key for IP address "
+ logit("%s host key for IP address "
"'%.128s' not in list of known hosts.",
type, ip);
else if (!add_host_to_hostfile(user_hostfile, ip,
- host_key))
- log("Failed to add the %s host key for IP "
+ host_key, options.hash_known_hosts))
+ logit("Failed to add the %s host key for IP "
"address '%.128s' to the list of known "
"hosts (%.30s).", type, ip, user_hostfile);
else
- log("Warning: Permanently added the %s host "
+ logit("Warning: Permanently added the %s host "
"key for IP address '%.128s' to the list "
"of known hosts.", type, ip);
}
"have requested strict checking.", type, host);
goto fail;
} else if (options.strict_host_key_checking == 2) {
+ char msg1[1024], msg2[1024];
+
+ if (show_other_keys(host, host_key))
+ snprintf(msg1, sizeof(msg1),
+ "\nbut keys of different type are already"
+ " known for this host.");
+ else
+ snprintf(msg1, sizeof(msg1), ".");
/* The default */
- char prompt[1024];
fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
- snprintf(prompt, sizeof(prompt),
+ msg2[0] = '\0';
+ if (options.verify_host_key_dns) {
+ if (matching_host_key_dns)
+ snprintf(msg2, sizeof(msg2),
+ "Matching host key fingerprint"
+ " found in DNS.\n");
+ else
+ snprintf(msg2, sizeof(msg2),
+ "No matching host key fingerprint"
+ " found in DNS.\n");
+ }
+ snprintf(msg, sizeof(msg),
"The authenticity of host '%.200s (%s)' can't be "
- "established.\n"
- "%s key fingerprint is %s.\n"
+ "established%s\n"
+ "%s key fingerprint is %s.\n%s"
"Are you sure you want to continue connecting "
- "(yes/no)? ", host, ip, type, fp);
+ "(yes/no)? ",
+ host, ip, msg1, type, fp, msg2);
xfree(fp);
- if (!confirm(prompt)) {
+ if (!confirm(msg))
goto fail;
- }
}
- if (options.check_host_ip && ip_status == HOST_NEW) {
- snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
- hostp = hostline;
- } else
- hostp = host;
-
/*
* If not in strict mode, add the key automatically to the
* local known_hosts file.
*/
- if (!add_host_to_hostfile(user_hostfile, hostp, host_key))
- log("Failed to add the host to the list of known "
+ if (options.check_host_ip && ip_status == HOST_NEW) {
+ snprintf(hostline, sizeof(hostline), "%s,%s",
+ host, ip);
+ hostp = hostline;
+ if (options.hash_known_hosts) {
+ /* Add hash of host and IP separately */
+ r = add_host_to_hostfile(user_hostfile, host,
+ host_key, options.hash_known_hosts) &&
+ add_host_to_hostfile(user_hostfile, ip,
+ host_key, options.hash_known_hosts);
+ } else {
+ /* Add unhashed "host,ip" */
+ r = add_host_to_hostfile(user_hostfile,
+ hostline, host_key,
+ options.hash_known_hosts);
+ }
+ } else {
+ r = add_host_to_hostfile(user_hostfile, host, host_key,
+ options.hash_known_hosts);
+ hostp = host;
+ }
+
+ if (!r)
+ logit("Failed to add the host to the list of known "
"hosts (%.500s).", user_hostfile);
else
- log("Warning: Permanently added '%.200s' (%s) to the "
+ logit("Warning: Permanently added '%.200s' (%s) to the "
"list of known hosts.", hostp, type);
break;
case HOST_CHANGED:
if (options.check_host_ip && host_ip_differ) {
- char *msg;
+ char *key_msg;
if (ip_status == HOST_NEW)
- msg = "is unknown";
+ key_msg = "is unknown";
else if (ip_status == HOST_OK)
- msg = "is unchanged";
+ key_msg = "is unchanged";
else
- msg = "has a different value";
+ key_msg = "has a different value";
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @");
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("The %s host key for %s has changed,", type, host);
error("and the key for the according IP address %s", ip);
- error("%s. This could either mean that", msg);
+ error("%s. This could either mean that", key_msg);
error("DNS SPOOFING is happening or the IP address for the host");
error("and its host key have changed at the same time.");
if (ip_status != HOST_NEW)
error("Offending key for IP in %s:%d", ip_file, ip_line);
}
/* The host key has changed. */
- fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
- error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
- error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
- error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
- error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
- error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
- error("It is also possible that the %s host key has just been changed.", type);
- error("The fingerprint for the %s key sent by the remote host is\n%s.",
- type, fp);
- error("Please contact your system administrator.");
+ warn_changed_key(host_key);
error("Add correct host key in %.100s to get rid of this message.",
user_hostfile);
error("Offending key in %s:%d", host_file, host_line);
- xfree(fp);
/*
* If strict host key checking is in use, the user will have
/*
* If strict host key checking has not been requested, allow
- * the connection but without password authentication or
+ * the connection but without MITM-able authentication or
* agent forwarding.
*/
if (options.password_authentication) {
"man-in-the-middle attacks.");
options.password_authentication = 0;
}
+ if (options.kbd_interactive_authentication) {
+ error("Keyboard-interactive authentication is disabled"
+ " to avoid man-in-the-middle attacks.");
+ options.kbd_interactive_authentication = 0;
+ options.challenge_response_authentication = 0;
+ }
+ if (options.challenge_response_authentication) {
+ error("Challenge/response authentication is disabled"
+ " to avoid man-in-the-middle attacks.");
+ options.challenge_response_authentication = 0;
+ }
if (options.forward_agent) {
error("Agent forwarding is disabled to avoid "
"man-in-the-middle attacks.");
error("Port forwarding is disabled to avoid "
"man-in-the-middle attacks.");
options.num_local_forwards =
- options.num_remote_forwards = 0;
+ options.num_remote_forwards = 0;
}
/*
* XXX Should permit the user to change to use the new id.
* accept the authentication.
*/
break;
+ case HOST_FOUND:
+ fatal("internal error");
+ break;
}
if (options.check_host_ip && host_status != HOST_CHANGED &&
ip_status == HOST_CHANGED) {
- log("Warning: the %s host key for '%.200s' "
- "differs from the key for the IP address '%.128s'",
- type, host, ip);
- if (host_status == HOST_OK)
- log("Matching host key in %s:%d", host_file, host_line);
- log("Offending key for IP in %s:%d", ip_file, ip_line);
+ snprintf(msg, sizeof(msg),
+ "Warning: the %s host key for '%.200s' "
+ "differs from the key for the IP address '%.128s'"
+ "\nOffending key for IP in %s:%d",
+ type, host, ip, ip_file, ip_line);
+ if (host_status == HOST_OK) {
+ len = strlen(msg);
+ snprintf(msg + len, sizeof(msg) - len,
+ "\nMatching host key in %s:%d",
+ host_file, host_line);
+ }
if (options.strict_host_key_checking == 1) {
+ logit("%s", msg);
error("Exiting, you have requested strict checking.");
goto fail;
} else if (options.strict_host_key_checking == 2) {
- if (!confirm("Are you sure you want "
- "to continue connecting (yes/no)? ")) {
+ strlcat(msg, "\nAre you sure you want "
+ "to continue connecting (yes/no)? ", sizeof(msg));
+ if (!confirm(msg))
goto fail;
- }
+ } else {
+ logit("%s", msg);
}
}
return -1;
}
+/* returns 0 if key verifies or -1 if key does NOT verify */
int
verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
{
struct stat st;
+ int flags = 0;
+
+ if (options.verify_host_key_dns &&
+ verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
+
+ if (flags & DNS_VERIFY_FOUND) {
+
+ if (options.verify_host_key_dns == 1 &&
+ flags & DNS_VERIFY_MATCH &&
+ flags & DNS_VERIFY_SECURE)
+ return 0;
+
+ if (flags & DNS_VERIFY_MATCH) {
+ matching_host_key_dns = 1;
+ } else {
+ warn_changed_key(host_key);
+ error("Update the SSHFP RR in DNS with the new "
+ "host key to get rid of this message.");
+ }
+ }
+ }
/* return ok if the key can be found in an old keyfile */
if (stat(options.system_hostfile2, &st) == 0 ||
* This function does not require super-user privileges.
*/
void
-ssh_login(Key **keys, int nkeys, const char *orighost,
+ssh_login(Sensitive *sensitive, const char *orighost,
struct sockaddr *hostaddr, struct passwd *pw)
{
char *host, *cp;
/* authenticate user */
if (compat20) {
ssh_kex2(host, hostaddr);
- ssh_userauth2(local_user, server_user, host, keys, nkeys);
+ ssh_userauth2(local_user, server_user, host, sensitive);
} else {
ssh_kex(host, hostaddr);
- ssh_userauth1(local_user, server_user, host, keys, nkeys);
+ ssh_userauth1(local_user, server_user, host, sensitive);
}
}
memset(padded, 0, size);
xfree(padded);
}
+
+static int
+show_key_from_file(const char *file, const char *host, int keytype)
+{
+ Key *found;
+ char *fp;
+ int line, ret;
+
+ found = key_new(keytype);
+ if ((ret = lookup_key_in_hostfile_by_type(file, host,
+ keytype, found, &line))) {
+ fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
+ logit("WARNING: %s key found for host %s\n"
+ "in %s:%d\n"
+ "%s key fingerprint %s.",
+ key_type(found), host, file, line,
+ key_type(found), fp);
+ xfree(fp);
+ }
+ key_free(found);
+ return (ret);
+}
+
+/* print all known host keys for a given host, but skip keys of given type */
+static int
+show_other_keys(const char *host, Key *key)
+{
+ int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1};
+ int i, found = 0;
+
+ for (i = 0; type[i] != -1; i++) {
+ if (type[i] == key->type)
+ continue;
+ if (type[i] != KEY_RSA1 &&
+ show_key_from_file(options.user_hostfile2, host, type[i])) {
+ found = 1;
+ continue;
+ }
+ if (type[i] != KEY_RSA1 &&
+ show_key_from_file(options.system_hostfile2, host, type[i])) {
+ found = 1;
+ continue;
+ }
+ if (show_key_from_file(options.user_hostfile, host, type[i])) {
+ found = 1;
+ continue;
+ }
+ if (show_key_from_file(options.system_hostfile, host, type[i])) {
+ found = 1;
+ continue;
+ }
+ debug2("no key of type %d for host %s", type[i], host);
+ }
+ return (found);
+}
+
+static void
+warn_changed_key(Key *host_key)
+{
+ char *fp;
+ const char *type = key_type(host_key);
+
+ fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+
+ error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
+ error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
+ error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
+ error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
+ error("It is also possible that the %s host key has just been changed.", type);
+ error("The fingerprint for the %s key sent by the remote host is\n%s.",
+ type, fp);
+ error("Please contact your system administrator.");
+
+ xfree(fp);
+}
extern char *client_version_string;
extern char *server_version_string;
extern Options options;
+extern Kex *xxx_kex;
+
+/* tty_flag is set in ssh.c. use this in ssh_userauth2 */
+/* if it is set then prevent the switch to the null cipher */
+
+extern int tty_flag;
/*
* SSH2 key exchange
pubkey_cleanup(&authctxt);
dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
-
+ if ((options.none_switch == 1) && !tty_flag) /* no null on tty sessions */
+ {
+ debug("Requesting none rekeying...");
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = "none";
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = "none";
+ kex_prop2buf(&xxx_kex->my,myproposal);
+ packet_request_rekeying();
+ fprintf(stderr, "WARNING: ENABLED NULL CIPHER\n");
+ }
debug("Authentication succeeded (%s).", authctxt.method->name);
}
major = PROTOCOL_MAJOR_1;
minor = PROTOCOL_MINOR_1;
}
- snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION);
+ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_RELEASE);
server_version_string = xstrdup(buf);
/* Send our protocol version identification. */
error("SessionGetInfo() failed with error %.8X",
(unsigned) err);
else
- debug("Current Session ID is %.8X / Session Attributes a
-re %.8X",
+ debug("Current Session ID is %.8X / Session Attributes are %.8X",
(unsigned) sid, (unsigned) sattrs);
if (inetd_flag && !(sattrs & sessionIsRoot))
error("SessionGetInfo() failed with error %.8X",
(unsigned) err);
else
- debug("New Session ID is %.8X / Session Attribut
-es are %.8X",
+ debug("New Session ID is %.8X / Session Attributes are %.8X",
(unsigned) sid, (unsigned) sattrs);
}
}
#define SSH_VERSION "OpenSSH_4.2"
#define SSH_PORTABLE "p1"
-#define SSH_RELEASE SSH_VERSION SSH_PORTABLE \
+#define SSH_HPN "-hpn"
+#define SSH_RELEASE SSH_VERSION SSH_PORTABLE SSH_HPN \
" NCSA_GSSAPI_20050905" \
GSI_VERSION KRB5_VERSION MGLUE_VERSION