* of its ports was connected to; and _you_ have to remember what
* local host:port pair went with that port number.
*
- * Hence: in SSH 1 this structure stores host:port pairs we intend
- * to allow connections to, and is indexed by those host:port
- * pairs. In SSH 2 it stores a mapping from source port to
- * destination host:port pair, and is indexed by source port.
+ * Hence, in SSH 1 this structure is indexed by destination
+ * host:port pair, whereas in SSH 2 it is indexed by source port.
*/
+struct ssh_portfwd; /* forward declaration */
+
struct ssh_rportfwd {
unsigned sport, dport;
char dhost[256];
+ char *sportdesc;
+ struct ssh_portfwd *pfrec;
+};
+#define free_rportfwd(pf) ( \
+ ((pf) ? (sfree((pf)->sportdesc)) : (void)0 ), sfree(pf) )
+
+/*
+ * Separately to the rportfwd tree (which is for looking up port
+ * open requests from the server), a tree of _these_ structures is
+ * used to keep track of all the currently open port forwardings,
+ * so that we can reconfigure in mid-session if the user requests
+ * it.
+ */
+struct ssh_portfwd {
+ int keep;
+ int type;
+ unsigned sport, dport;
+ char *saddr, *daddr;
+ struct ssh_rportfwd *remote;
+ void *local;
};
+#define free_portfwd(pf) ( \
+ ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr)) : (void)0 ), sfree(pf) )
struct Packet {
long length;
};
typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin);
+typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx);
+
+struct queued_handler;
+struct queued_handler {
+ int msg1, msg2;
+ chandler_fn_t handler;
+ void *ctx;
+ struct queued_handler *next;
+};
struct ssh_tag {
const struct plug_function_table *fn;
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
- tree234 *rportfwds;
+ tree234 *rportfwds, *portfwds;
enum {
SSH_STATE_PREPACKET,
int userpass_input_bufpos;
int userpass_input_echo;
- char *portfwd_strptr;
int pkt_ctx;
void *x11auth;
handler_fn_t packet_dispatch[256];
/*
+ * Queues of one-off handler functions for success/failure
+ * indications from a request.
+ */
+ struct queued_handler *qhead, *qtail;
+
+ /*
* This module deals with sending keepalives.
*/
Pinger pinger;
* size-based rekeys.
*/
unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
+ unsigned long max_data_size;
int kex_in_progress;
- long next_rekey;
+ long next_rekey, last_rekey;
};
-#define MAX_DATA_BEFORE_REKEY (0x40000000UL)
-#define REKEY_TIMEOUT (3600 * TICKSPERSEC)
-
#define logevent(s) logevent(ssh->frontend, s)
/* logevent, only printf-formatted. */
return 0;
}
+/*
+ * Special form of strcmp which can cope with NULL inputs. NULL is
+ * defined to sort before even the empty string.
+ */
+static int nullstrcmp(const char *a, const char *b)
+{
+ if (a == NULL && b == NULL)
+ return 0;
+ if (a == NULL)
+ return -1;
+ if (b == NULL)
+ return +1;
+ return strcmp(a, b);
+}
+
+static int ssh_portcmp(void *av, void *bv)
+{
+ struct ssh_portfwd *a = (struct ssh_portfwd *) av;
+ struct ssh_portfwd *b = (struct ssh_portfwd *) bv;
+ int i;
+ if (a->type > b->type)
+ return +1;
+ if (a->type < b->type)
+ return -1;
+ if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->sport > b->sport)
+ return +1;
+ if (a->sport < b->sport)
+ return -1;
+ if (a->type != 'D') {
+ if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->dport > b->dport)
+ return +1;
+ if (a->dport < b->dport)
+ return -1;
+ }
+ return 0;
+}
+
static int alloc_channel_id(Ssh ssh)
{
const unsigned CHANNEL_NUMBER_OFFSET = 256;
ssh->outgoing_data_size += pkt->encrypted_len;
if (!ssh->kex_in_progress &&
- ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY)
+ ssh->max_data_size != 0 &&
+ ssh->outgoing_data_size > ssh->max_data_size)
do_ssh2_transport(ssh, "Initiating key re-exchange "
"(too much data sent)", -1, NULL);
ssh->outgoing_data_size += ssh->deferred_data_size;
if (!ssh->kex_in_progress &&
- ssh->outgoing_data_size > MAX_DATA_BEFORE_REKEY)
+ ssh->max_data_size != 0 &&
+ ssh->outgoing_data_size > ssh->max_data_size)
do_ssh2_transport(ssh, "Initiating key re-exchange "
"(too much data sent)", -1, NULL);
ssh->deferred_data_size = 0;
}
}
+static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin)
+{
+ struct queued_handler *qh = ssh->qhead;
+
+ assert(qh != NULL);
+
+ assert(pktin->type == qh->msg1 || pktin->type == qh->msg2);
+
+ if (qh->msg1 > 0) {
+ assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler);
+ ssh->packet_dispatch[qh->msg1] = NULL;
+ }
+ if (qh->msg2 > 0) {
+ assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler);
+ ssh->packet_dispatch[qh->msg2] = NULL;
+ }
+
+ if (qh->next) {
+ ssh->qhead = qh->next;
+
+ if (ssh->qhead->msg1 > 0) {
+ assert(ssh->packet_dispatch[ssh->qhead->msg1] == NULL);
+ ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler;
+ }
+ if (ssh->qhead->msg2 > 0) {
+ assert(ssh->packet_dispatch[ssh->qhead->msg2] == NULL);
+ ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler;
+ }
+ } else {
+ ssh->qhead = ssh->qtail = NULL;
+ ssh->packet_dispatch[pktin->type] = NULL;
+ }
+
+ qh->handler(ssh, pktin, qh->ctx);
+
+ sfree(qh);
+}
+
+static void ssh_queue_handler(Ssh ssh, int msg1, int msg2,
+ chandler_fn_t handler, void *ctx)
+{
+ struct queued_handler *qh;
+
+ qh = snew(struct queued_handler);
+ qh->msg1 = msg1;
+ qh->msg2 = msg2;
+ qh->handler = handler;
+ qh->ctx = ctx;
+ qh->next = NULL;
+
+ if (ssh->qtail == NULL) {
+ ssh->qhead = qh;
+
+ if (qh->msg1 > 0) {
+ assert(ssh->packet_dispatch[qh->msg1] == NULL);
+ ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler;
+ }
+ if (qh->msg2 > 0) {
+ assert(ssh->packet_dispatch[qh->msg2] == NULL);
+ ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler;
+ }
+ } else {
+ ssh->qtail->next = qh;
+ }
+ ssh->qtail = qh;
+}
+
+static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx)
+{
+ struct ssh_rportfwd *rpf, *pf = (struct ssh_rportfwd *)ctx;
+
+ if (pktin->type == (ssh->version == 1 ? SSH1_SMSG_SUCCESS :
+ SSH2_MSG_REQUEST_SUCCESS)) {
+ logeventf(ssh, "Remote port forwarding from %s enabled",
+ pf->sportdesc);
+ } else {
+ logeventf(ssh, "Remote port forwarding from %s refused",
+ pf->sportdesc);
+
+ rpf = del234(ssh->rportfwds, pf);
+ assert(rpf == pf);
+ free_rportfwd(pf);
+ }
+}
+
+static void ssh_setup_portfwd(Ssh ssh, const Config *cfg)
+{
+ char type;
+ int n;
+ int sport,dport,sserv,dserv;
+ char sports[256], dports[256], saddr[256], host[256];
+ const char *portfwd_strptr;
+
+ portfwd_strptr = cfg->portfwd;
+
+ if (!ssh->portfwds) {
+ ssh->portfwds = newtree234(ssh_portcmp);
+ } else {
+ /*
+ * Go through the existing port forwardings and tag them
+ * with keep==FALSE. Any that we want to keep will be
+ * re-enabled as we go through the configuration and find
+ * out which bits are the same as they were before.
+ */
+ struct ssh_portfwd *epf;
+ int i;
+ for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
+ epf->keep = FALSE;
+ }
+
+ while (*portfwd_strptr) {
+ type = *portfwd_strptr++;
+ saddr[0] = '\0';
+ n = 0;
+ while (*portfwd_strptr && *portfwd_strptr != '\t') {
+ if (*portfwd_strptr == ':') {
+ /*
+ * We've seen a colon in the middle of the
+ * source port number. This means that
+ * everything we've seen until now is the
+ * source _address_, so we'll move it into
+ * saddr and start sports from the beginning
+ * again.
+ */
+ portfwd_strptr++;
+ sports[n] = '\0';
+ if (ssh->version == 1 && type == 'R') {
+ logeventf(ssh, "SSH1 cannot handle remote source address "
+ "spec \"%s\"; ignoring", sports);
+ } else
+ strcpy(saddr, sports);
+ n = 0;
+ }
+ if (n < 255) sports[n++] = *portfwd_strptr++;
+ }
+ sports[n] = 0;
+ if (type != 'D') {
+ if (*portfwd_strptr == '\t')
+ portfwd_strptr++;
+ n = 0;
+ while (*portfwd_strptr && *portfwd_strptr != ':') {
+ if (n < 255) host[n++] = *portfwd_strptr++;
+ }
+ host[n] = 0;
+ if (*portfwd_strptr == ':')
+ portfwd_strptr++;
+ n = 0;
+ while (*portfwd_strptr) {
+ if (n < 255) dports[n++] = *portfwd_strptr++;
+ }
+ dports[n] = 0;
+ portfwd_strptr++;
+ dport = atoi(dports);
+ dserv = 0;
+ if (dport == 0) {
+ dserv = 1;
+ dport = net_service_lookup(dports);
+ if (!dport) {
+ logeventf(ssh, "Service lookup failed for destination"
+ " port \"%s\"", dports);
+ }
+ }
+ } else {
+ while (*portfwd_strptr) portfwd_strptr++;
+ dport = dserv = -1;
+ portfwd_strptr++; /* eat the NUL and move to next one */
+ }
+ sport = atoi(sports);
+ sserv = 0;
+ if (sport == 0) {
+ sserv = 1;
+ sport = net_service_lookup(sports);
+ if (!sport) {
+ logeventf(ssh, "Service lookup failed for source"
+ " port \"%s\"", sports);
+ }
+ }
+ if (sport && dport) {
+ /* Set up a description of the source port. */
+ struct ssh_portfwd *pfrec, *epfrec;
+ char *sportdesc;
+ sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s",
+ (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
+ (int)(*saddr?1:0), ":",
+ (int)(sserv ? strlen(sports) : 0), sports,
+ sserv, "(", sport, sserv, ")");
+
+ pfrec = snew(struct ssh_portfwd);
+ pfrec->type = type;
+ pfrec->saddr = *saddr ? dupstr(saddr) : NULL;
+ pfrec->sport = sport;
+ pfrec->daddr = dupstr(host);
+ pfrec->dport = dport;
+ pfrec->local = NULL;
+ pfrec->remote = NULL;
+
+ epfrec = add234(ssh->portfwds, pfrec);
+ if (epfrec != pfrec) {
+ /*
+ * We already have a port forwarding with precisely
+ * these parameters. Hence, no need to do anything;
+ * simply tag the existing one as `keep'.
+ */
+ epfrec->keep = TRUE;
+ free_portfwd(pfrec);
+ } else if (type == 'L') {
+ /* Verbose description of the destination port */
+ char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s",
+ host,
+ (int)(dserv ? strlen(dports) : 0), dports,
+ dserv, "(", dport, dserv, ")");
+ const char *err = pfd_addforward(host, dport,
+ *saddr ? saddr : NULL,
+ sport, ssh, &ssh->cfg,
+ &pfrec->local);
+ if (err) {
+ logeventf(ssh, "Local port %s forward to %s"
+ " failed: %s", sportdesc, dportdesc, err);
+ } else {
+ logeventf(ssh, "Local port %s forwarding to %s",
+ sportdesc, dportdesc);
+ }
+ sfree(dportdesc);
+ } else if (type == 'D') {
+ const char *err = pfd_addforward(NULL, -1,
+ *saddr ? saddr : NULL,
+ sport, ssh, &ssh->cfg,
+ &pfrec->local);
+ if (err) {
+ logeventf(ssh, "Local port %s SOCKS dynamic forward"
+ " setup failed: %s", sportdesc, err);
+ } else {
+ logeventf(ssh, "Local port %s doing SOCKS"
+ " dynamic forwarding", sportdesc);
+ }
+ } else {
+ struct ssh_rportfwd *pf;
+
+ /*
+ * Ensure the remote port forwardings tree exists.
+ */
+ if (!ssh->rportfwds) {
+ if (ssh->version == 1)
+ ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
+ else
+ ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
+ }
+
+ pf = snew(struct ssh_rportfwd);
+ strcpy(pf->dhost, host);
+ pf->dport = dport;
+ pf->sport = sport;
+ if (add234(ssh->rportfwds, pf) != pf) {
+ logeventf(ssh, "Duplicate remote port forwarding to %s:%d",
+ host, dport);
+ sfree(pf);
+ } else {
+ logeventf(ssh, "Requesting remote port %s"
+ " forward to %s:%.*s%.*s%d%.*s",
+ sportdesc, host,
+ (int)(dserv ? strlen(dports) : 0), dports,
+ dserv, "(", dport, dserv, ")");
+
+ pf->sportdesc = sportdesc;
+ sportdesc = NULL;
+ pfrec->remote = pf;
+ pf->pfrec = pfrec;
+
+ if (ssh->version == 1) {
+ send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,
+ PKT_INT, sport,
+ PKT_STR, host,
+ PKT_INT, dport,
+ PKT_END);
+ ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
+ SSH1_SMSG_FAILURE,
+ ssh_rportfwd_succfail, pf);
+ } else {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ ssh2_pkt_addstring(pktout, "tcpip-forward");
+ ssh2_pkt_addbool(pktout, 1);/* want reply */
+ if (*saddr) {
+ ssh2_pkt_addstring(pktout, saddr);
+ } else if (ssh->cfg.rport_acceptall) {
+ ssh2_pkt_addstring(pktout, "0.0.0.0");
+ } else {
+ ssh2_pkt_addstring(pktout, "127.0.0.1");
+ }
+ ssh2_pkt_adduint32(pktout, sport);
+ ssh2_pkt_send(ssh, pktout);
+
+ ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
+ SSH2_MSG_REQUEST_FAILURE,
+ ssh_rportfwd_succfail, pf);
+ }
+ }
+ }
+ sfree(sportdesc);
+ }
+ }
+
+ /*
+ * Now go through and destroy any port forwardings which were
+ * not re-enabled.
+ */
+ {
+ struct ssh_portfwd *epf;
+ int i;
+ for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
+ if (!epf->keep) {
+ char *message;
+
+ message = dupprintf("%s port forwarding from %s%s%d",
+ epf->type == 'L' ? "local" :
+ epf->type == 'R' ? "remote" : "dynamic",
+ epf->saddr ? epf->saddr : "",
+ epf->saddr ? ":" : "",
+ epf->sport);
+
+ if (epf->type != 'D') {
+ char *msg2 = dupprintf("%s to %s:%d", message,
+ epf->daddr, epf->dport);
+ sfree(message);
+ message = msg2;
+ }
+
+ logeventf(ssh, "Cancelling %s", message);
+ sfree(message);
+
+ if (epf->remote) {
+ struct ssh_rportfwd *rpf = epf->remote;
+ struct Packet *pktout;
+
+ /*
+ * Cancel the port forwarding at the server
+ * end.
+ */
+ if (ssh->version == 1) {
+ /*
+ * We cannot cancel listening ports on the
+ * server side in SSH1! There's no message
+ * to support it. Instead, we simply remove
+ * the rportfwd record from the local end
+ * so that any connections the server tries
+ * to make on it are rejected.
+ */
+ } else {
+ pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ ssh2_pkt_addstring(pktout, "cancel-tcpip-forward");
+ ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */
+ if (epf->saddr) {
+ ssh2_pkt_addstring(pktout, epf->saddr);
+ } else if (ssh->cfg.rport_acceptall) {
+ ssh2_pkt_addstring(pktout, "0.0.0.0");
+ } else {
+ ssh2_pkt_addstring(pktout, "127.0.0.1");
+ }
+ ssh2_pkt_adduint32(pktout, epf->sport);
+ ssh2_pkt_send(ssh, pktout);
+ }
+
+ del234(ssh->rportfwds, rpf);
+ free_rportfwd(rpf);
+ } else if (epf->local) {
+ pfd_terminate(epf->local);
+ }
+
+ delpos234(ssh->portfwds, i);
+ free_portfwd(epf);
+ i--; /* so we don't skip one in the list */
+ }
+ }
+}
+
static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)
{
char *string;
/* Data sent down one of our channels. */
int i = ssh_pkt_getuint32(pktin);
char *p;
- int len;
+ unsigned int len;
struct ssh_channel *c;
ssh_pkt_getstring(pktin, &p, &len);
/* Data for an agent message. Buffer it. */
while (len > 0) {
if (c->u.a.lensofar < 4) {
- int l = min(4 - c->u.a.lensofar, len);
+ unsigned int l = min(4 - c->u.a.lensofar, len);
memcpy(c->u.a.msglen + c->u.a.lensofar, p,
l);
p += l;
memcpy(c->u.a.message, c->u.a.msglen, 4);
}
if (c->u.a.lensofar >= 4 && len > 0) {
- int l =
+ unsigned int l =
min(c->u.a.totallen - c->u.a.lensofar,
len);
memcpy(c->u.a.message + c->u.a.lensofar, p,
}
}
- {
- char type;
- int n;
- int sport,dport,sserv,dserv;
- char sports[256], dports[256], saddr[256], host[256];
-
- ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
- /* Add port forwardings. */
- ssh->portfwd_strptr = ssh->cfg.portfwd;
- while (*ssh->portfwd_strptr) {
- type = *ssh->portfwd_strptr++;
- saddr[0] = '\0';
- n = 0;
- while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t') {
- if (*ssh->portfwd_strptr == ':') {
- /*
- * We've seen a colon in the middle of the
- * source port number. This means that
- * everything we've seen until now is the
- * source _address_, so we'll move it into
- * saddr and start sports from the beginning
- * again.
- */
- ssh->portfwd_strptr++;
- sports[n] = '\0';
- strcpy(saddr, sports);
- n = 0;
- }
- if (n < 255) sports[n++] = *ssh->portfwd_strptr++;
- }
- sports[n] = 0;
- if (type != 'D') {
- if (*ssh->portfwd_strptr == '\t')
- ssh->portfwd_strptr++;
- n = 0;
- while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') {
- if (n < 255) host[n++] = *ssh->portfwd_strptr++;
- }
- host[n] = 0;
- if (*ssh->portfwd_strptr == ':')
- ssh->portfwd_strptr++;
- n = 0;
- while (*ssh->portfwd_strptr) {
- if (n < 255) dports[n++] = *ssh->portfwd_strptr++;
- }
- dports[n] = 0;
- ssh->portfwd_strptr++;
- dport = atoi(dports);
- dserv = 0;
- if (dport == 0) {
- dserv = 1;
- dport = net_service_lookup(dports);
- if (!dport) {
- logeventf(ssh, "Service lookup failed for"
- " destination port \"%s\"", dports);
- }
- }
- } else {
- while (*ssh->portfwd_strptr) ssh->portfwd_strptr++;
- dport = dserv = -1;
- ssh->portfwd_strptr++; /* eat the NUL and move to next one */
- }
- sport = atoi(sports);
- sserv = 0;
- if (sport == 0) {
- sserv = 1;
- sport = net_service_lookup(sports);
- if (!sport) {
- logeventf(ssh, "Service lookup failed for source"
- " port \"%s\"", sports);
- }
- }
- if (sport && dport) {
- /* Set up a description of the source port. */
- static char *sportdesc;
- sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s",
- (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")");
- if (type == 'L') {
- /* Verbose description of the destination port */
- char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s",
- host,
- (int)(dserv ? strlen(dports) : 0), dports,
- dserv, "(", dport, dserv, ")");
- const char *err = pfd_addforward(host, dport,
- *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- if (err) {
- logeventf(ssh, "Local port %s forward to %s"
- " failed: %s", sportdesc, dportdesc, err);
- } else {
- logeventf(ssh, "Local port %s forwarding to %s",
- sportdesc, dportdesc);
- }
- sfree(dportdesc);
- } else if (type == 'D') {
- const char *err = pfd_addforward(NULL, -1,
- *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- if (err) {
- logeventf(ssh, "Local port %s SOCKS dynamic forward"
- " setup failed: %s", sportdesc, err);
- } else {
- logeventf(ssh, "Local port %s doing SOCKS"
- " dynamic forwarding", sportdesc);
- }
- } else {
- struct ssh_rportfwd *pf;
- pf = snew(struct ssh_rportfwd);
- strcpy(pf->dhost, host);
- pf->dport = dport;
- if (*saddr) {
- logeventf(ssh,
- "SSH1 cannot handle source address spec \"%s:%d\"; ignoring",
- saddr, sport);
- }
- if (add234(ssh->rportfwds, pf) != pf) {
- logeventf(ssh,
- "Duplicate remote port forwarding to %s:%d",
- host, dport);
- sfree(pf);
- } else {
- logeventf(ssh, "Requesting remote port %.*s%.*s%d%.*s"
- " forward to %s:%.*s%.*s%d%.*s",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")",
- host,
- (int)(dserv ? strlen(dports) : 0), dports,
- dserv, "(", dport, dserv, ")");
- send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,
- PKT_INT, sport,
- PKT_STR, host,
- PKT_INT, dport,
- PKT_END);
- do {
- crReturnV;
- } while (!pktin);
- if (pktin->type != SSH1_SMSG_SUCCESS
- && pktin->type != SSH1_SMSG_FAILURE) {
- bombout(("Protocol confusion"));
- crStopV;
- } else if (pktin->type == SSH1_SMSG_FAILURE) {
- c_write_str(ssh, "Server refused port"
- " forwarding\r\n");
- logevent("Server refused this port forwarding");
- } else {
- logevent("Remote port forwarding enabled");
- ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] =
- ssh1_msg_port_open;
- }
- }
- }
- sfree(sportdesc);
- }
- }
- }
+ ssh_setup_portfwd(ssh, &ssh->cfg);
+ ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
if (!ssh->cfg.nopty) {
/* Unpick the terminal-speed string. */
* Key exchange is over. Schedule a timer for our next rekey.
*/
ssh->kex_in_progress = FALSE;
- ssh->next_rekey = schedule_timer(REKEY_TIMEOUT, ssh2_timer, ssh);
-
+ ssh->last_rekey = GETTICKCOUNT();
+ if (ssh->cfg.ssh_rekey_time != 0)
+ ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+ ssh2_timer, ssh);
+
/*
* If this is the first key exchange phase, we must pass the
* SSH2_MSG_NEWKEYS packet to the next layer, not because it
static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
{
char *data;
- int length;
+ unsigned int length;
unsigned i = ssh_pkt_getuint32(pktin);
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
case CHAN_AGENT:
while (length > 0) {
if (c->u.a.lensofar < 4) {
- int l = min(4 - c->u.a.lensofar, length);
+ unsigned int l = min(4 - c->u.a.lensofar, length);
memcpy(c->u.a.msglen + c->u.a.lensofar,
data, l);
data += l;
memcpy(c->u.a.message, c->u.a.msglen, 4);
}
if (c->u.a.lensofar >= 4 && length > 0) {
- int l =
+ unsigned int l =
min(c->u.a.totallen - c->u.a.lensofar,
length);
memcpy(c->u.a.message + c->u.a.lensofar,
/*
* Enable port forwardings.
*/
- {
- char type;
- int n;
- int sport,dport,sserv,dserv;
- char sports[256], dports[256], saddr[256], host[256];
-
- ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
- /* Add port forwardings. */
- ssh->portfwd_strptr = ssh->cfg.portfwd;
- while (*ssh->portfwd_strptr) {
- type = *ssh->portfwd_strptr++;
- saddr[0] = '\0';
- n = 0;
- while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t') {
- if (*ssh->portfwd_strptr == ':') {
- /*
- * We've seen a colon in the middle of the
- * source port number. This means that
- * everything we've seen until now is the
- * source _address_, so we'll move it into
- * saddr and start sports from the beginning
- * again.
- */
- ssh->portfwd_strptr++;
- sports[n] = '\0';
- strcpy(saddr, sports);
- n = 0;
- }
- if (n < 255) sports[n++] = *ssh->portfwd_strptr++;
- }
- sports[n] = 0;
- if (type != 'D') {
- if (*ssh->portfwd_strptr == '\t')
- ssh->portfwd_strptr++;
- n = 0;
- while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') {
- if (n < 255) host[n++] = *ssh->portfwd_strptr++;
- }
- host[n] = 0;
- if (*ssh->portfwd_strptr == ':')
- ssh->portfwd_strptr++;
- n = 0;
- while (*ssh->portfwd_strptr) {
- if (n < 255) dports[n++] = *ssh->portfwd_strptr++;
- }
- dports[n] = 0;
- ssh->portfwd_strptr++;
- dport = atoi(dports);
- dserv = 0;
- if (dport == 0) {
- dserv = 1;
- dport = net_service_lookup(dports);
- if (!dport) {
- logeventf(ssh, "Service lookup failed for destination"
- " port \"%s\"", dports);
- }
- }
- } else {
- while (*ssh->portfwd_strptr) ssh->portfwd_strptr++;
- dport = dserv = -1;
- ssh->portfwd_strptr++; /* eat the NUL and move to next one */
- }
- sport = atoi(sports);
- sserv = 0;
- if (sport == 0) {
- sserv = 1;
- sport = net_service_lookup(sports);
- if (!sport) {
- logeventf(ssh, "Service lookup failed for source"
- " port \"%s\"", sports);
- }
- }
- if (sport && dport) {
- /* Set up a description of the source port. */
- static char *sportdesc;
- sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s",
- (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")");
- if (type == 'L') {
- /* Verbose description of the destination port */
- char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s",
- host,
- (int)(dserv ? strlen(dports) : 0), dports,
- dserv, "(", dport, dserv, ")");
- const char *err = pfd_addforward(host, dport,
- *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- if (err) {
- logeventf(ssh, "Local port %s forward to %s"
- " failed: %s", sportdesc, dportdesc, err);
- } else {
- logeventf(ssh, "Local port %s forwarding to %s",
- sportdesc, dportdesc);
- }
- sfree(dportdesc);
- } else if (type == 'D') {
- const char *err = pfd_addforward(NULL, -1,
- *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- if (err) {
- logeventf(ssh, "Local port %s SOCKS dynamic forward"
- " setup failed: %s", sportdesc, err);
- } else {
- logeventf(ssh, "Local port %s doing SOCKS"
- " dynamic forwarding", sportdesc);
- }
- } else {
- struct ssh_rportfwd *pf;
- pf = snew(struct ssh_rportfwd);
- strcpy(pf->dhost, host);
- pf->dport = dport;
- pf->sport = sport;
- if (add234(ssh->rportfwds, pf) != pf) {
- logeventf(ssh, "Duplicate remote port forwarding"
- " to %s:%d", host, dport);
- sfree(pf);
- } else {
- logeventf(ssh, "Requesting remote port %s"
- " forward to %s:%.*s%.*s%d%.*s",
- sportdesc,
- host,
- (int)(dserv ? strlen(dports) : 0), dports,
- dserv, "(", dport, dserv, ")");
- s->pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
- ssh2_pkt_addstring(s->pktout, "tcpip-forward");
- ssh2_pkt_addbool(s->pktout, 1);/* want reply */
- if (*saddr) {
- ssh2_pkt_addstring(s->pktout, saddr);
- } else if (ssh->cfg.rport_acceptall) {
- ssh2_pkt_addstring(s->pktout, "0.0.0.0");
- } else {
- ssh2_pkt_addstring(s->pktout, "127.0.0.1");
- }
- ssh2_pkt_adduint32(s->pktout, sport);
- ssh2_pkt_send(ssh, s->pktout);
-
- crWaitUntilV(pktin);
-
- if (pktin->type != SSH2_MSG_REQUEST_SUCCESS) {
- if (pktin->type != SSH2_MSG_REQUEST_FAILURE) {
- bombout(("Unexpected response to port "
- "forwarding request: packet type %d",
- pktin->type));
- crStopV;
- }
- logevent("Server refused this port forwarding");
- } else {
- logevent("Remote port forwarding enabled");
- }
- }
- }
- sfree(sportdesc);
- }
- }
- }
+ ssh_setup_portfwd(ssh, &ssh->cfg);
/*
* Potentially enable agent forwarding.
{
Ssh ssh = (Ssh)ctx;
- if (!ssh->kex_in_progress &&
+ if (!ssh->kex_in_progress && ssh->cfg.ssh_rekey_time != 0 &&
now - ssh->next_rekey >= 0) {
do_ssh2_transport(ssh, "Initiating key re-exchange (timeout)",
-1, NULL);
if (pktin) {
ssh->incoming_data_size += pktin->encrypted_len;
if (!ssh->kex_in_progress &&
- ssh->incoming_data_size > MAX_DATA_BEFORE_REKEY)
+ ssh->max_data_size != 0 &&
+ ssh->incoming_data_size > ssh->max_data_size)
do_ssh2_transport(ssh, "Initiating key re-exchange "
"(too much data received)", -1, NULL);
}
ssh->queue = NULL;
ssh->queuelen = ssh->queuesize = 0;
ssh->queueing = FALSE;
+ ssh->qhead = ssh->qtail = NULL;
*backend_handle = ssh;
ssh->channels = NULL;
ssh->rportfwds = NULL;
+ ssh->portfwds = NULL;
ssh->send_ok = 0;
ssh->editing = 0;
ssh->incoming_data_size = ssh->outgoing_data_size =
ssh->deferred_data_size = 0L;
+ ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
ssh->kex_in_progress = FALSE;
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
ssh_free_packet(ssh->queue[ssh->queuelen]);
sfree(ssh->queue);
+ while (ssh->qhead) {
+ struct queued_handler *qh = ssh->qhead;
+ ssh->qhead = qh->next;
+ sfree(ssh->qhead);
+ }
+ ssh->qhead = ssh->qtail = NULL;
+
if (ssh->channels) {
while ((c = delpos234(ssh->channels, 0)) != NULL) {
switch (c->type) {
/*
* Reconfigure the SSH backend.
- *
- * Currently, this function does nothing very useful. In future,
- * however, we could do some handy things with it. For example, we
- * could make the port forwarding configurer active in the Change
- * Settings box, and this routine could close down existing
- * forwardings and open up new ones in response to changes.
*/
static void ssh_reconfig(void *handle, Config *cfg)
{
Ssh ssh = (Ssh) handle;
+ char *rekeying = NULL;
+ unsigned long old_max_data_size;
+
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
+ ssh_setup_portfwd(ssh, cfg);
+
+ if (ssh->cfg.ssh_rekey_time != cfg->ssh_rekey_time &&
+ cfg->ssh_rekey_time != 0) {
+ long new_next = ssh->last_rekey + cfg->ssh_rekey_time*60*TICKSPERSEC;
+ long now = GETTICKCOUNT();
+
+ if (new_next - now < 0) {
+ rekeying = "Initiating key re-exchange (timeout shortened)";
+ } else {
+ ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh);
+ }
+ }
+
+ old_max_data_size = ssh->max_data_size;
+ ssh->max_data_size = parse_blocksize(cfg->ssh_rekey_data);
+ if (old_max_data_size != ssh->max_data_size &&
+ ssh->max_data_size != 0) {
+ if (ssh->outgoing_data_size > ssh->max_data_size ||
+ ssh->incoming_data_size > ssh->max_data_size)
+ rekeying = "Initiating key re-exchange (data limit lowered)";
+ }
+
+ if (rekeying && !ssh->kex_in_progress) {
+ do_ssh2_transport(ssh, rekeying, -1, NULL);
+ }
+
ssh->cfg = *cfg; /* STRUCTURE COPY */
}
}
/*
+ * cfg_info for SSH is the currently running version of the
+ * protocol. (1 for 1; 2 for 2; 0 for not-decided-yet.)
+ */
+static int ssh_cfg_info(void *handle)
+{
+ Ssh ssh = (Ssh) handle;
+ return ssh->version;
+}
+
+/*
* Gross hack: pscp will try to start SFTP but fall back to scp1 if
* that fails. This variable is the means by which scp.c can reach
* into the SSH code and find out which one it got.
ssh_provide_ldisc,
ssh_provide_logctx,
ssh_unthrottle,
+ ssh_cfg_info,
22
};