1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00

Implement zlib@openssh.com, using the rekey-after-userauth method suggested in

the wishlist entry.

[originally from svn r9120]
[this svn revision also touched putty-website,putty-wishlist]
This commit is contained in:
Jacob Nevins 2011-03-04 22:34:47 +00:00
parent d0b99ccee3
commit 74c5f7dda9
3 changed files with 104 additions and 25 deletions

125
ssh.c
View File

@ -544,7 +544,7 @@ static int ssh_comp_none_disable(void *handle)
return 0; return 0;
} }
const static struct ssh_compress ssh_comp_none = { 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_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 ssh_comp_none_disable, NULL
@ -5422,6 +5422,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
int n_preferred_ciphers; int n_preferred_ciphers;
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX]; const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
const struct ssh_compress *preferred_comp; const struct ssh_compress *preferred_comp;
int userauth_succeeded; /* for delayed compression */
int pending_compression;
int got_session_id, activated_authconn; int got_session_id, activated_authconn;
struct Packet *pktout; struct Packet *pktout;
int dlgret; int dlgret;
@ -5437,6 +5439,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
s->cscomp_tobe = s->sccomp_tobe = NULL; s->cscomp_tobe = s->sccomp_tobe = NULL;
s->got_session_id = s->activated_authconn = FALSE; 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. * Be prepared to work around the buggy MAC problem.
@ -5601,26 +5605,32 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
if (i < s->nmacs - 1) if (i < s->nmacs - 1)
ssh2_pkt_addstring_str(s->pktout, ","); ssh2_pkt_addstring_str(s->pktout, ",");
} }
/* List client->server compression algorithms. */ /* List client->server compression algorithms,
ssh2_pkt_addstring_start(s->pktout); * then server->client compression algorithms. (We use the
assert(lenof(compressions) > 1); * same set twice.) */
ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name); for (j = 0; j < 2; j++) {
for (i = 0; i < lenof(compressions); i++) { ssh2_pkt_addstring_start(s->pktout);
const struct ssh_compress *c = compressions[i]; assert(lenof(compressions) > 1);
if (c != s->preferred_comp) { /* 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, ",");
ssh2_pkt_addstring_str(s->pktout, c->name); ssh2_pkt_addstring_str(s->pktout,
s->preferred_comp->delayed_name);
} }
} for (i = 0; i < lenof(compressions); i++) {
/* List server->client compression algorithms. */ const struct ssh_compress *c = compressions[i];
ssh2_pkt_addstring_start(s->pktout); if (c != s->preferred_comp) {
assert(lenof(compressions) > 1); ssh2_pkt_addstring_str(s->pktout, ",");
ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name); ssh2_pkt_addstring_str(s->pktout, c->name);
for (i = 0; i < lenof(compressions); i++) { if (s->userauth_succeeded && c->delayed_name) {
const struct ssh_compress *c = compressions[i]; ssh2_pkt_addstring_str(s->pktout, ",");
if (c != s->preferred_comp) { ssh2_pkt_addstring_str(s->pktout, c->delayed_name);
ssh2_pkt_addstring_str(s->pktout, ","); }
ssh2_pkt_addstring_str(s->pktout, c->name); }
} }
} }
/* List client->server languages. Empty list. */ /* List client->server languages. Empty list. */
@ -5769,6 +5779,13 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
if (in_commasep_string(c->name, str, len)) { if (in_commasep_string(c->name, str, len)) {
s->cscomp_tobe = c; s->cscomp_tobe = c;
break; 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 */ ssh_pkt_getstring(pktin, &str, &len); /* server->client compression */
@ -5778,8 +5795,19 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
if (in_commasep_string(c->name, str, len)) { if (in_commasep_string(c->name, str, len)) {
s->sccomp_tobe = c; s->sccomp_tobe = c;
break; 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); /* client->server language */
ssh_pkt_getstring(pktin, &str, &len); /* server->client language */ ssh_pkt_getstring(pktin, &str, &len); /* server->client language */
s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok; s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
@ -6315,19 +6343,52 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
* start. * start.
* *
* We _also_ go back to the start if we see pktin==NULL and * 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 * `initiate client-driven rekey', and `in' contains a message
* giving the reason for the rekey. * 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) || while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||
(!pktin && inlen == -1))) { (!pktin && inlen < 0))) {
wait_for_rekey: wait_for_rekey:
crReturn(1); crReturn(1);
} }
if (pktin) { if (pktin) {
logevent("Server initiated key re-exchange"); logevent("Server initiated key re-exchange");
} else { } 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 * Special case: if the server bug is set that doesn't
* allow rekeying, we give a different log message and * allow rekeying, we give a different log message and
* continue waiting. (If such a server _initiates_ a rekey, * continue waiting. (If such a server _initiates_ a rekey,
@ -6345,7 +6406,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC, schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
ssh2_timer, ssh); ssh2_timer, ssh);
} }
goto wait_for_rekey; /* this is utterly horrid */ goto wait_for_rekey; /* this is still utterly horrid */
} else { } else {
logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in); logeventf(ssh, "Initiating key re-exchange (%s)", (char *)in);
} }
@ -7256,7 +7317,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
int tried_gssapi; int tried_gssapi;
#endif #endif
int kbd_inter_refused; int kbd_inter_refused;
int we_are_in; int we_are_in, userauth_success;
prompts_t *cur_prompt; prompts_t *cur_prompt;
int num_prompts; int num_prompts;
char username[100]; char username[100];
@ -7292,7 +7353,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
crBegin(ssh->do_ssh2_authconn_crstate); crBegin(ssh->do_ssh2_authconn_crstate);
s->done_service_req = FALSE; s->done_service_req = FALSE;
s->we_are_in = FALSE; s->we_are_in = s->userauth_success = FALSE;
#ifndef NO_GSSAPI #ifndef NO_GSSAPI
s->tried_gssapi = FALSE; s->tried_gssapi = FALSE;
#endif #endif
@ -7582,7 +7643,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
} }
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) { if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted"); logevent("Access granted");
s->we_are_in = TRUE; s->we_are_in = s->userauth_success = TRUE;
break; break;
} }
@ -8590,6 +8651,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
if (s->agent_response) if (s->agent_response)
sfree(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. * Now the connection protocol has started, one way or another.
*/ */

3
ssh.h
View File

@ -251,6 +251,9 @@ struct ssh_signkey {
struct ssh_compress { struct ssh_compress {
char *name; char *name;
/* For zlib@openssh.com: if non-NULL, this name will be considered once
* userauth has completed successfully. */
char *delayed_name;
void *(*compress_init) (void); void *(*compress_init) (void);
void (*compress_cleanup) (void *); void (*compress_cleanup) (void *);
int (*compress) (void *, unsigned char *block, int len, int (*compress) (void *, unsigned char *block, int len,

View File

@ -1371,6 +1371,7 @@ int main(int argc, char **argv)
const struct ssh_compress ssh_zlib = { const struct ssh_compress ssh_zlib = {
"zlib", "zlib",
"zlib@openssh.com", /* delayed version */
zlib_compress_init, zlib_compress_init,
zlib_compress_cleanup, zlib_compress_cleanup,
zlib_compress_block, zlib_compress_block,