return 0;
}
const static struct ssh_compress ssh_comp_none = {
- "none",
+ "none", NULL,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
ssh_comp_none_disable, NULL
* Fully qualified host name, which we need if doing GSSAPI.
*/
char *fullhostname;
+
+#ifndef NO_GSSAPI
+ /*
+ * GSSAPI libraries for this session.
+ */
+ struct ssh_gss_liblist *gsslibs;
+#endif
};
#define logevent(s) logevent(ssh->frontend, s)
x11_close(c->u.x11.s);
break;
case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
pfd_close(c->u.pfd.s);
break;
}
int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
const struct ssh_compress *preferred_comp;
+ int userauth_succeeded; /* for delayed compression */
+ int pending_compression;
int got_session_id, activated_authconn;
struct Packet *pktout;
int dlgret;
s->cscomp_tobe = s->sccomp_tobe = NULL;
s->got_session_id = s->activated_authconn = FALSE;
+ s->userauth_succeeded = FALSE;
+ s->pending_compression = FALSE;
/*
* Be prepared to work around the buggy MAC problem.
if (i < s->nmacs - 1)
ssh2_pkt_addstring_str(s->pktout, ",");
}
- /* List client->server compression algorithms. */
- ssh2_pkt_addstring_start(s->pktout);
- assert(lenof(compressions) > 1);
- ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
- for (i = 0; i < lenof(compressions); i++) {
- const struct ssh_compress *c = compressions[i];
- if (c != s->preferred_comp) {
+ /* List client->server compression algorithms,
+ * then server->client compression algorithms. (We use the
+ * same set twice.) */
+ for (j = 0; j < 2; j++) {
+ ssh2_pkt_addstring_start(s->pktout);
+ assert(lenof(compressions) > 1);
+ /* Prefer non-delayed versions */
+ ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
+ /* We don't even list delayed versions of algorithms until
+ * they're allowed to be used, to avoid a race. See the end of
+ * this function. */
+ if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
ssh2_pkt_addstring_str(s->pktout, ",");
- ssh2_pkt_addstring_str(s->pktout, c->name);
+ ssh2_pkt_addstring_str(s->pktout,
+ s->preferred_comp->delayed_name);
}
- }
- /* List server->client compression algorithms. */
- ssh2_pkt_addstring_start(s->pktout);
- assert(lenof(compressions) > 1);
- ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
- for (i = 0; i < lenof(compressions); i++) {
- const struct ssh_compress *c = compressions[i];
- if (c != s->preferred_comp) {
- ssh2_pkt_addstring_str(s->pktout, ",");
- ssh2_pkt_addstring_str(s->pktout, c->name);
+ for (i = 0; i < lenof(compressions); i++) {
+ const struct ssh_compress *c = compressions[i];
+ if (c != s->preferred_comp) {
+ ssh2_pkt_addstring_str(s->pktout, ",");
+ ssh2_pkt_addstring_str(s->pktout, c->name);
+ if (s->userauth_succeeded && c->delayed_name) {
+ ssh2_pkt_addstring_str(s->pktout, ",");
+ ssh2_pkt_addstring_str(s->pktout, c->delayed_name);
+ }
+ }
}
}
/* List client->server languages. Empty list. */
if (in_commasep_string(c->name, str, len)) {
s->cscomp_tobe = c;
break;
+ } else if (in_commasep_string(c->delayed_name, str, len)) {
+ if (s->userauth_succeeded) {
+ s->cscomp_tobe = c;
+ break;
+ } else {
+ s->pending_compression = TRUE; /* try this later */
+ }
}
}
ssh_pkt_getstring(pktin, &str, &len); /* server->client compression */
if (in_commasep_string(c->name, str, len)) {
s->sccomp_tobe = c;
break;
+ } else if (in_commasep_string(c->delayed_name, str, len)) {
+ if (s->userauth_succeeded) {
+ s->sccomp_tobe = c;
+ break;
+ } else {
+ s->pending_compression = TRUE; /* try this later */
+ }
}
}
+ if (s->pending_compression) {
+ logevent("Server supports delayed compression; "
+ "will try this later");
+ }
ssh_pkt_getstring(pktin, &str, &len); /* client->server language */
ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
* start.
*
* We _also_ go back to the start if we see pktin==NULL and
- * inlen==-1, because this is a special signal meaning
+ * inlen negative, because this is a special signal meaning
* `initiate client-driven rekey', and `in' contains a message
* giving the reason for the rekey.
+ *
+ * inlen==-1 means always initiate a rekey;
+ * inlen==-2 means that userauth has completed successfully and
+ * we should consider rekeying (for delayed compression).
*/
while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||
- (!pktin && inlen == -1))) {
+ (!pktin && inlen < 0))) {
wait_for_rekey:
crReturn(1);
}
if (pktin) {
logevent("Server initiated key re-exchange");
} else {
+ if (inlen == -2) {
+ /*
+ * authconn has seen a USERAUTH_SUCCEEDED. Time to enable
+ * delayed compression, if it's available.
+ *
+ * draft-miller-secsh-compression-delayed-00 says that you
+ * negotiate delayed compression in the first key exchange, and
+ * both sides start compressing when the server has sent
+ * USERAUTH_SUCCESS. This has a race condition -- the server
+ * can't know when the client has seen it, and thus which incoming
+ * packets it should treat as compressed.
+ *
+ * Instead, we do the initial key exchange without offering the
+ * delayed methods, but note if the server offers them; when we
+ * get here, if a delayed method was available that was higher
+ * on our list than what we got, we initiate a rekey in which we
+ * _do_ list the delayed methods (and hopefully get it as a
+ * result). Subsequent rekeys will do the same.
+ */
+ assert(!s->userauth_succeeded); /* should only happen once */
+ s->userauth_succeeded = TRUE;
+ if (!s->pending_compression)
+ /* Can't see any point rekeying. */
+ goto wait_for_rekey; /* this is utterly horrid */
+ /* else fall through to rekey... */
+ s->pending_compression = FALSE;
+ }
/*
+ * Now we've decided to rekey.
+ *
* Special case: if the server bug is set that doesn't
* allow rekeying, we give a different log message and
* continue waiting. (If such a server _initiates_ a rekey,
schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
ssh2_timer, ssh);
}
- goto wait_for_rekey; /* this is utterly horrid */
+ goto wait_for_rekey; /* this is still utterly horrid */
} else {
logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in);
}
}
/*
- * Buffer banner messages for later display at some convenient point.
+ * Buffer banner messages for later display at some convenient point,
+ * if we're going to display them.
*/
static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
{
/* Arbitrary limit to prevent unbounded inflation of buffer */
- if (bufchain_size(&ssh->banner) <= 131072) {
+ if (ssh->cfg.ssh_show_banner &&
+ bufchain_size(&ssh->banner) <= 131072) {
char *banner = NULL;
int size = 0;
ssh_pkt_getstring(pktin, &banner, &size);
int tried_gssapi;
#endif
int kbd_inter_refused;
- int we_are_in;
+ int we_are_in, userauth_success;
prompts_t *cur_prompt;
int num_prompts;
char username[100];
crBegin(ssh->do_ssh2_authconn_crstate);
s->done_service_req = FALSE;
- s->we_are_in = FALSE;
+ s->we_are_in = s->userauth_success = FALSE;
#ifndef NO_GSSAPI
s->tried_gssapi = FALSE;
#endif
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
- s->we_are_in = TRUE;
+ s->we_are_in = s->userauth_success = TRUE;
break;
}
in_commasep_string("password", methods, methlen);
s->can_keyb_inter = ssh->cfg.try_ki_auth &&
in_commasep_string("keyboard-interactive", methods, methlen);
-#ifndef NO_GSSAPI
- ssh_gss_init();
+#ifndef NO_GSSAPI
+ if (!ssh->gsslibs)
+ ssh->gsslibs = ssh_gss_setup(&ssh->cfg);
s->can_gssapi = ssh->cfg.try_gssapi_auth &&
in_commasep_string("gssapi-with-mic", methods, methlen) &&
- n_ssh_gss_libraries > 0;
+ ssh->gsslibs->nlibraries > 0;
#endif
}
s->gsslib = NULL;
for (i = 0; i < ngsslibs; i++) {
int want_id = ssh->cfg.ssh_gsslist[i];
- for (j = 0; j < n_ssh_gss_libraries; j++)
- if (ssh_gss_libraries[j].id == want_id) {
- s->gsslib = &ssh_gss_libraries[j];
+ for (j = 0; j < ssh->gsslibs->nlibraries; j++)
+ if (ssh->gsslibs->libraries[j].id == want_id) {
+ s->gsslib = &ssh->gsslibs->libraries[j];
goto got_gsslib; /* double break */
}
}
if (s->agent_response)
sfree(s->agent_response);
+ if (s->userauth_success) {
+ /*
+ * We've just received USERAUTH_SUCCESS, and we haven't sent any
+ * packets since. Signal the transport layer to consider enacting
+ * delayed compression.
+ *
+ * (Relying on we_are_in is not sufficient, as
+ * draft-miller-secsh-compression-delayed is quite clear that it
+ * triggers on USERAUTH_SUCCESS specifically, and we_are_in can
+ * become set for other reasons.)
+ */
+ do_ssh2_transport(ssh, "enabling delayed compression", -2, NULL);
+ }
+
/*
* Now the connection protocol has started, one way or another.
*/
ssh->max_data_size = parse_blocksize(ssh->cfg.ssh_rekey_data);
ssh->kex_in_progress = FALSE;
+#ifndef NO_GSSAPI
+ ssh->gsslibs = NULL;
+#endif
+
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
if (p != NULL)
return p;
x11_close(c->u.x11.s);
break;
case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
if (c->u.pfd.s != NULL)
pfd_close(c->u.pfd.s);
break;
if (ssh->pinger)
pinger_free(ssh->pinger);
bufchain_clear(&ssh->queued_incoming_data);
+#ifndef NO_GSSAPI
+ if (ssh->gsslibs)
+ ssh_gss_cleanup(ssh->gsslibs);
+#endif
sfree(ssh);
random_unref();