X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/9fca3f8c1d548f46bcbcccb7807912049afb456e..42af6a672d16302ee1b8971a7cc164120f7572ab:/ssh.c diff --git a/ssh.c b/ssh.c index f78ffbef..84a66e0e 100644 --- a/ssh.c +++ b/ssh.c @@ -12,6 +12,7 @@ #include "putty.h" #include "tree234.h" #include "ssh.h" +#include "sshgss.h" #ifndef FALSE #define FALSE 0 @@ -62,6 +63,10 @@ #define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 /* 0x47 */ #define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 /* 0x48 */ +#define SSH1_AUTH_RHOSTS 1 /* 0x1 */ +#define SSH1_AUTH_RSA 2 /* 0x2 */ +#define SSH1_AUTH_PASSWORD 3 /* 0x3 */ +#define SSH1_AUTH_RHOSTS_RSA 4 /* 0x4 */ #define SSH1_AUTH_TIS 5 /* 0x5 */ #define SSH1_AUTH_CCARD 16 /* 0x10 */ @@ -108,6 +113,12 @@ #define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */ #define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */ #define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */ +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 /* * Packet type contexts, so that ssh2_pkt_type can correctly decode @@ -123,6 +134,7 @@ typedef enum { SSH2_PKTCTX_NOAUTH, SSH2_PKTCTX_PUBLICKEY, SSH2_PKTCTX_PASSWORD, + SSH2_PKTCTX_GSSAPI, SSH2_PKTCTX_KBDINTER } Pkt_ACtx; @@ -179,6 +191,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_DERIVEKEY 32 #define BUG_SSH2_REKEY 64 #define BUG_SSH2_PK_SESSIONID 128 +#define BUG_SSH2_MAXPKT 256 /* * Codes for terminal modes. @@ -334,6 +347,12 @@ static char *ssh1_pkt_type(int type) } static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) { + translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI); + translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI); translate(SSH2_MSG_DISCONNECT); translate(SSH2_MSG_IGNORE); translate(SSH2_MSG_UNIMPLEMENTED); @@ -529,6 +548,14 @@ enum { /* channel types */ }; /* + * little structure to keep track of outstanding WINDOW_ADJUSTs + */ +struct winadj { + struct winadj *next; + unsigned size; +}; + +/* * 2-3-4 tree storing channels. */ struct ssh_channel { @@ -548,15 +575,29 @@ struct ssh_channel { * A channel is completely finished with when all four bits are set. */ int closes; + /* + * True if this channel is causing the underlying connection to be + * throttled. + */ + int throttling_conn; union { - struct ssh1_data_channel { - int throttling; - } v1; struct ssh2_data_channel { bufchain outbuffer; unsigned remwindow, remmaxpkt; /* locwindow is signed so we can cope with excess data. */ int locwindow, locmaxwin; + /* + * remlocwin is the amount of local window that we think + * the remote end had available to it after it sent the + * last data packet or window adjust ack. + */ + int remlocwin; + /* + * These store the list of window adjusts that haven't + * been acked. + */ + struct winadj *winadj_head, *winadj_tail; + enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; union { @@ -786,7 +827,7 @@ struct ssh_tag { void *x11auth; int version; - int v1_throttle_count; + int conn_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; @@ -869,6 +910,11 @@ struct ssh_tag { int kex_in_progress; long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ + + /* + * Fully qualified host name, which we need if doing GSSAPI. + */ + char *fullhostname; }; #define logevent(s) logevent(ssh->frontend, s) @@ -2365,6 +2411,17 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_SSH2_REKEY; logevent("We believe remote version has SSH-2 rekey bug"); } + + if (ssh->cfg.sshbug_maxpkt2 == FORCE_ON || + (ssh->cfg.sshbug_maxpkt2 == AUTO && + (wc_match("1.36_sshlib GlobalSCAPE", imp) || + wc_match("1.36 sshlib: GlobalScape", imp)))) { + /* + * This version ignores our makpkt and needs to be throttled. + */ + ssh->remote_bugs |= BUG_SSH2_MAXPKT; + logevent("We believe remote version ignores SSH-2 maximum packet size"); + } } /* @@ -2800,12 +2857,30 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, SockAddr addr; const char *err; - ssh->savedhost = snewn(1 + strlen(host), char); - strcpy(ssh->savedhost, host); + if (*ssh->cfg.loghost) { + char *colon; + + ssh->savedhost = dupstr(ssh->cfg.loghost); + ssh->savedport = 22; /* default ssh port */ - if (port < 0) - port = 22; /* default ssh port */ - ssh->savedport = port; + /* + * A colon suffix on savedhost also lets us affect + * savedport. + * + * (FIXME: do something about IPv6 address literals here.) + */ + colon = strrchr(ssh->savedhost, ':'); + if (colon) { + *colon++ = '\0'; + if (*colon) + ssh->savedport = atoi(colon); + } + } else { + ssh->savedhost = dupstr(host); + if (port < 0) + port = 22; /* default ssh port */ + ssh->savedport = port; + } /* * Try to find host. @@ -2819,6 +2894,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, sk_addr_free(addr); return err; } + ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */ /* * Open socket. @@ -2843,20 +2919,28 @@ static const char *connect_to_host(Ssh ssh, char *host, int port, ssh_send_verstring(ssh, NULL); } + /* + * loghost, if configured, overrides realhost. + */ + if (*ssh->cfg.loghost) { + sfree(*realhost); + *realhost = dupstr(ssh->cfg.loghost); + } + return NULL; } /* * Throttle or unthrottle the SSH connection. */ -static void ssh1_throttle(Ssh ssh, int adjust) +static void ssh_throttle_conn(Ssh ssh, int adjust) { - int old_count = ssh->v1_throttle_count; - ssh->v1_throttle_count += adjust; - assert(ssh->v1_throttle_count >= 0); - if (ssh->v1_throttle_count && !old_count) { + int old_count = ssh->conn_throttle_count; + ssh->conn_throttle_count += adjust; + assert(ssh->conn_throttle_count >= 0); + if (ssh->conn_throttle_count && !old_count) { ssh_set_frozen(ssh, 1); - } else if (!ssh->v1_throttle_count && old_count) { + } else if (!ssh->conn_throttle_count && old_count) { ssh_set_frozen(ssh, 0); } } @@ -3066,6 +3150,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin); s->supported_ciphers_mask = ssh_pkt_getuint32(pktin); s->supported_auths_mask = ssh_pkt_getuint32(pktin); + if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) + s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA); ssh->v1_local_protoflags = ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED; @@ -3319,7 +3405,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, crWaitUntil(pktin); - if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) { /* We must not attempt PK auth. Pretend we've already tried it. */ s->tried_publickey = s->tried_agent = 1; } else { @@ -3776,6 +3862,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } } if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) { + bombout(("No supported authentication methods available")); + crStop(0); + } s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", @@ -4024,17 +4114,20 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len) void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) { Ssh ssh = c->ssh; + int buflimit; if (ssh->state == SSH_STATE_CLOSED) return; if (ssh->version == 1) { - if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 0; - ssh1_throttle(ssh, -1); - } + buflimit = SSH1_BUFFER_LIMIT; } else { - ssh2_set_window(c, c->v.v2.locmaxwin - bufsize); + buflimit = c->v.v2.locmaxwin; + ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0); + } + if (c->throttling_conn && bufsize <= buflimit) { + c->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); } } @@ -4463,7 +4556,7 @@ static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin) string, stringlen); if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 1; - ssh1_throttle(ssh, +1); + ssh_throttle_conn(ssh, +1); } } @@ -4497,7 +4590,7 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_X11; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4526,7 +4619,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; add234(ssh->channels, c); @@ -4580,7 +4673,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin) c->halfopen = FALSE; c->localid = alloc_channel_id(ssh); c->closes = 0; - c->v.v1.throttling = 0; + c->throttling_conn = 0; c->type = CHAN_SOCKDATA; /* identify channel type */ add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, @@ -4602,7 +4695,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) c->remoteid = localid; c->halfopen = FALSE; c->type = CHAN_SOCKDATA; - c->v.v1.throttling = 0; + c->throttling_conn = 0; pfd_confirm(c->u.pfd.s); } @@ -4738,9 +4831,9 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) bufsize = 0; /* agent channels never back up */ break; } - if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) { - c->v.v1.throttling = 1; - ssh1_throttle(ssh, +1); + if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); } } } @@ -6170,6 +6263,22 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c) } /* + * Set up most of a new ssh_channel for SSH-2. + */ +static void ssh2_channel_init(struct ssh_channel *c) +{ + Ssh ssh = c->ssh; + c->localid = alloc_channel_id(ssh); + c->closes = 0; + c->throttling_conn = FALSE; + c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin = + ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.throttle_state = UNTHROTTLED; + bufchain_init(&c->v.v2.outbuffer); +} + +/* * Potentially enlarge the window on an SSH-2 channel. */ static void ssh2_set_window(struct ssh_channel *c, int newwin) @@ -6185,6 +6294,15 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) return; /* + * If the remote end has a habit of ignoring maxpkt, limit the + * window so that it has no choice (assuming it doesn't ignore the + * window as well). + */ + if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT) + newwin = OUR_V2_MAXPKT; + + + /* * Only send a WINDOW_ADJUST if there's significantly more window * available than the other end thinks there is. This saves us * sending a WINDOW_ADJUST for every character in a shell session. @@ -6193,7 +6311,51 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; + struct winadj *wa; + /* + * In order to keep track of how much window the client + * actually has available, we'd like it to acknowledge each + * WINDOW_ADJUST. We can't do that directly, so we accompany + * it with a CHANNEL_REQUEST that has to be acknowledged. + * + * This is only necessary if we're opening the window wide. + * If we're not, then throughput is being constrained by + * something other than the maximum window size anyway. + * + * We also only send this if the main channel has finished its + * initial CHANNEL_REQUESTs and installed the default + * CHANNEL_FAILURE handler, so as not to risk giving it + * unexpected CHANNEL_FAILUREs. + */ + if (newwin == c->v.v2.locmaxwin && + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) { + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org"); + ssh2_pkt_addbool(pktout, TRUE); + ssh2_pkt_send(ssh, pktout); + + /* + * CHANNEL_FAILURE doesn't come with any indication of + * what message caused it, so we have to keep track of the + * outstanding CHANNEL_REQUESTs ourselves. + */ + wa = snew(struct winadj); + wa->size = newwin - c->v.v2.locwindow; + wa->next = NULL; + if (!c->v.v2.winadj_head) + c->v.v2.winadj_head = wa; + else + c->v.v2.winadj_tail->next = wa; + c->v.v2.winadj_tail = wa; + if (c->v.v2.throttle_state != UNTHROTTLED) + c->v.v2.throttle_state = UNTHROTTLING; + } else { + /* Pretend the WINDOW_ADJUST was acked immediately. */ + c->v.v2.remlocwin = newwin; + c->v.v2.throttle_state = THROTTLED; + } pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow); @@ -6202,12 +6364,94 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) } } +/* + * Find the channel associated with a message. If there's no channel, + * or it's not properly open, make a noise about it and return NULL. + */ +static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin) +{ + unsigned localid = ssh_pkt_getuint32(pktin); + struct ssh_channel *c; + + c = find234(ssh->channels, &localid, ssh_channelfind); + if (!c || + (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION && + pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) { + char *buf = dupprintf("Received %s for %s channel %u", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type), + c ? "half-open" : "nonexistent", localid); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); + return NULL; + } + return c; +} + +static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin) +{ + /* + * This should never get called. All channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + struct ssh_channel *c; + struct winadj *wa; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + wa = c->v.v2.winadj_head; + if (wa) + ssh_disconnect(ssh, NULL, "Received SSH_MSG_CHANNEL_SUCCESS for " + "\"winadj@putty.projects.tartarus.org\"", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + else + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_SUCCESS", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); +} + +static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) +{ + /* + * The only time this should get called is for "winadj@putty" + * messages sent above. All other channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + struct ssh_channel *c; + struct winadj *wa; + + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + wa = c->v.v2.winadj_head; + if (!wa) { + ssh_disconnect(ssh, NULL, + "Received unsolicited SSH_MSG_CHANNEL_FAILURE", + SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + return; + } + c->v.v2.winadj_head = wa->next; + c->v.v2.remlocwin += wa->size; + sfree(wa); + /* + * winadj messages are only sent when the window is fully open, so + * if we get an ack of one, we know any pending unthrottle is + * complete. + */ + if (c->v.v2.throttle_state == UNTHROTTLING) + c->v.v2.throttle_state = UNTHROTTLED; +} + static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); - if (c && !c->closes) { + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; + if (!c->closes) { c->v.v2.remwindow += ssh_pkt_getuint32(pktin); ssh2_try_send_and_unthrottle(c); } @@ -6217,11 +6461,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) { char *data; int length; - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA && ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR) return; /* extended but not stderr */ @@ -6229,6 +6472,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (data) { int bufsize = 0; c->v.v2.locwindow -= length; + c->v.v2.remlocwin -= length; switch (c->type) { case CHAN_MAINSESSION: bufsize = @@ -6286,6 +6530,14 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) break; } /* + * If it looks like the remote end hit the end of its window, + * and we didn't want it to do that, think about using a + * larger window. + */ + if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED && + c->v.v2.locmaxwin < 0x40000000) + c->v.v2.locmaxwin += OUR_V2_WINSIZE; + /* * If we are not buffering too much data, * enlarge the window again at the remote side. * If we are buffering too much, we may still @@ -6294,17 +6546,27 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) */ ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ? c->v.v2.locmaxwin - bufsize : 0); + /* + * If we're either buffering way too much data, or if we're + * buffering anything at all and we're in "simple" mode, + * throttle the whole channel. + */ + if ((bufsize > c->v.v2.locmaxwin || + (ssh->cfg.ssh_simple && bufsize > 0)) && + !c->throttling_conn) { + c->throttling_conn = 1; + ssh_throttle_conn(ssh, +1); + } } } static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type == CHAN_X11) { /* @@ -6323,16 +6585,12 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); - if (!c || c->halfopen) { - bombout(("Received CHANNEL_CLOSE for %s channel %d\n", - c ? "half-open" : "nonexistent", i)); + c = ssh2_channel_msg(ssh, pktin); + if (!c) return; - } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: @@ -6385,13 +6643,12 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin) { - unsigned i = ssh_pkt_getuint32(pktin); struct ssh_channel *c; struct Packet *pktout; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're confirming this */ c->remoteid = ssh_pkt_getuint32(pktin); @@ -6423,14 +6680,13 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) "Unknown channel type", "Resource shortage", }; - unsigned i = ssh_pkt_getuint32(pktin); unsigned reason_code; char *reason_string; int reason_length; struct ssh_channel *c; - c = find234(ssh->channels, &i, ssh_channelfind); + c = ssh2_channel_msg(ssh, pktin); if (!c) - return; /* nonexistent channel */ + return; if (c->type != CHAN_SOCKDATA_DORMANT) return; /* dunno why they're failing this */ @@ -6449,31 +6705,19 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin) static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) { - unsigned localid; char *type; int typelen, want_reply; int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */ struct ssh_channel *c; struct Packet *pktout; - localid = ssh_pkt_getuint32(pktin); + c = ssh2_channel_msg(ssh, pktin); + if (!c) + return; ssh_pkt_getstring(pktin, &type, &typelen); want_reply = ssh2_pkt_getbool(pktin); /* - * First, check that the channel exists. Otherwise, - * we can instantly disconnect with a rude message. - */ - c = find234(ssh->channels, &localid, ssh_channelfind); - if (!c) { - char *buf = dupprintf("Received channel request for nonexistent" - " channel %d", localid); - ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); - sfree(buf); - return; - } - - /* * Having got the channel number, we now look at * the request type string to see if it's something * we recognise. @@ -6500,7 +6744,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) int msglen = 0, core = FALSE; /* ICK: older versions of OpenSSH (e.g. 3.4p1) * provide an `int' for the signal, despite its - * having been a `string' in the drafts since at + * having been a `string' in the drafts of RFC 4254 since at * least 2001. (Fixed in session.c 1.147.) Try to * infer which we can safely parse it as. */ { @@ -6543,7 +6787,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin) fmt_sig = dupprintf(" %d", signum); ssh->exitcode = 128 + signum; } else { - /* As per the drafts. */ + /* As per RFC 4254. */ char *sig; int siglen; ssh_pkt_getstring(pktin, &sig, &siglen); @@ -6758,12 +7002,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Rejected channel open: %s", error); sfree(c); } else { - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; + ssh2_channel_init(c); c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; - bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); ssh2_pkt_adduint32(pktout, c->remoteid); @@ -6822,12 +7063,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, AUTH_TYPE_PUBLICKEY_OFFER_LOUD, AUTH_TYPE_PUBLICKEY_OFFER_QUIET, AUTH_TYPE_PASSWORD, + AUTH_TYPE_GSSAPI, AUTH_TYPE_KEYBOARD_INTERACTIVE, AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET } type; int done_service_req; int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter; int tried_pubkey_config, done_agent; + int can_gssapi; + int tried_gssapi; int kbd_inter_refused; int we_are_in; prompts_t *cur_prompt; @@ -6851,6 +7095,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int try_send; int num_env, env_left, env_ok; struct Packet *pktout; + Ssh_gss_ctx gss_ctx; + Ssh_gss_buf gss_buf; + Ssh_gss_buf gss_rcvtok, gss_sndtok; + Ssh_gss_name gss_srv_name; + Ssh_gss_stat gss_stat; }; crState(do_ssh2_authconn_state); @@ -6858,6 +7107,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->done_service_req = FALSE; s->we_are_in = FALSE; + s->tried_gssapi = FALSE; + if (!ssh->cfg.ssh_no_userauth) { /* * Request userauth protocol, and await a response to it. @@ -7145,7 +7396,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, break; } - if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) { + if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) { bombout(("Strange packet received during authentication: " "type %d", pktin->type)); crStopV; @@ -7216,6 +7467,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, 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 + s->can_gssapi = ssh->cfg.try_gssapi_auth && + in_commasep_string("gssapi-with-mic", methods, methlen) && + ssh_gss_init(); +#endif } ssh->pkt_actx = SSH2_PKTCTX_NOAUTH; @@ -7544,6 +7800,157 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, key->alg->freekey(key->data); } +#ifndef NO_GSSAPI + } else if (s->can_gssapi && !s->tried_gssapi) { + + /* GSSAPI Authentication */ + + int micoffset; + Ssh_gss_buf mic; + s->type = AUTH_TYPE_GSSAPI; + s->tried_gssapi = TRUE; + s->gotit = TRUE; + ssh->pkt_actx = SSH2_PKTCTX_GSSAPI; + + /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); + ssh2_pkt_addstring(s->pktout, s->username); + ssh2_pkt_addstring(s->pktout, "ssh-connection"); + ssh2_pkt_addstring(s->pktout, "gssapi-with-mic"); + + /* add mechanism info */ + ssh_gss_indicate_mech(&s->gss_buf); + + /* number of GSSAPI mechanisms */ + ssh2_pkt_adduint32(s->pktout,1); + + /* length of OID + 2 */ + ssh2_pkt_adduint32(s->pktout, s->gss_buf.len + 2); + ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE); + + /* length of OID */ + ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.len); + + ssh_pkt_adddata(s->pktout, s->gss_buf.data, s->gss_buf.len); + ssh2_pkt_send(ssh, s->pktout); + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) { + logevent("GSSAPI authentication request refused"); + continue; + } + + /* check returned packet ... */ + + ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len); + if (s->gss_rcvtok.len != s->gss_buf.len + 2 || + s->gss_rcvtok.data[0] != SSH2_GSS_OIDTYPE || + s->gss_rcvtok.data[1] != s->gss_buf.len || + memcmp(s->gss_rcvtok.data+2,s->gss_buf.data,s->gss_buf.len) ) { + logevent("GSSAPI authentication - wrong response from server"); + continue; + } + + /* now start running */ + s->gss_stat = ssh_gss_import_name(ssh->fullhostname, + &s->gss_srv_name); + if (s->gss_stat != SSH_GSS_OK) { + if (s->gss_stat == SSH_GSS_BAD_HOST_NAME) + logevent("GSSAPI import name failed - Bad service name"); + else + logevent("GSSAPI import name failed"); + continue; + } + + /* fetch TGT into GSS engine */ + s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx); + + if (s->gss_stat != SSH_GSS_OK) { + logevent("GSSAPI authentication failed to get credentials"); + ssh_gss_release_name(&s->gss_srv_name); + continue; + } + + /* initial tokens are empty */ + s->gss_rcvtok.len = s->gss_sndtok.len = 0; + s->gss_rcvtok.data = s->gss_sndtok.data = NULL; + + /* now enter the loop */ + do { + s->gss_stat = ssh_gss_init_sec_context(&s->gss_ctx, + s->gss_srv_name, + ssh->cfg.gssapifwd, + &s->gss_rcvtok, + &s->gss_sndtok); + + if (s->gss_stat!=SSH_GSS_S_COMPLETE && + s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) { + logevent("GSSAPI authentication initialisation failed"); + + if (ssh_gss_display_status(s->gss_ctx,&s->gss_buf) == SSH_GSS_OK) { + logevent(s->gss_buf.data); + sfree(s->gss_buf.data); + } + + break; + } + logevent("GSSAPI authentication initialised"); + + /* Client and server now exchange tokens until GSSAPI + * no longer says CONTINUE_NEEDED */ + + if (s->gss_sndtok.len != 0) { + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.data,s->gss_sndtok.len); + ssh2_pkt_send(ssh, s->pktout); + ssh_gss_free_tok(&s->gss_sndtok); + } + + if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) { + crWaitUntilV(pktin); + if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) { + logevent("GSSAPI authentication - bad server response"); + s->gss_stat = SSH_GSS_FAILURE; + break; + } + ssh_pkt_getstring(pktin,&s->gss_rcvtok.data,&s->gss_rcvtok.len); + } + } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED); + + if (s->gss_stat != SSH_GSS_OK) { + ssh_gss_release_name(&s->gss_srv_name); + ssh_gss_release_cred(&s->gss_ctx); + continue; + } + logevent("GSSAPI authentication loop finished OK"); + + /* Now send the MIC */ + + s->pktout = ssh2_pkt_init(0); + micoffset = s->pktout->length; + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout, (char *)ssh->v2_session_id, ssh->v2_session_id_len); + ssh_pkt_addbyte(s->pktout, SSH2_MSG_USERAUTH_REQUEST); + ssh_pkt_addstring(s->pktout, s->username); + ssh_pkt_addstring(s->pktout, "ssh-connection"); + ssh_pkt_addstring(s->pktout, "gssapi-with-mic"); + + s->gss_buf.data = (char *)s->pktout->data + micoffset; + s->gss_buf.len = s->pktout->length - micoffset; + + ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic); + s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC); + ssh_pkt_addstring_start(s->pktout); + ssh_pkt_addstring_data(s->pktout, mic.data, mic.len); + ssh2_pkt_send(ssh, s->pktout); + ssh_gss_free_mic(&mic); + + s->gotit = FALSE; + + ssh_gss_release_name(&s->gss_srv_name); + ssh_gss_release_cred(&s->gss_ctx); + continue; +#endif } else if (s->can_keyb_inter && !s->kbd_inter_refused) { /* @@ -7964,15 +8371,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); logeventf(ssh, "Opening direct-tcpip channel to %s:%d in place of session", ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = - ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -7999,10 +8404,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened direct-tcpip channel"); @@ -8010,12 +8413,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; - ssh->mainchan->localid = alloc_channel_id(ssh); + ssh2_channel_init(ssh->mainchan); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = - ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -8032,10 +8433,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); ssh->mainchan->halfopen = FALSE; ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->closes = 0; ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - bufchain_init(&ssh->mainchan->v.v2.outbuffer); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened channel for session"); @@ -8329,6 +8728,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_special(ssh, TS_EOF); /* + * All the initial channel requests are done, so install the default + * failure handler. + */ + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_success; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; + + /* * Transfer data! */ if (ssh->ldisc) @@ -8376,7 +8782,7 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) { /* log reason code in disconnect message */ char *buf, *msg; - int nowlen, reason, msglen; + int reason, msglen; reason = ssh_pkt_getuint32(pktin); ssh_pkt_getstring(pktin, &msg, &msglen); @@ -8390,14 +8796,14 @@ static void ssh2_msg_disconnect(Ssh ssh, struct Packet *pktin) } logevent(buf); sfree(buf); - buf = dupprintf("Disconnection message text: %n%.*s", - &nowlen, msglen, msg); + buf = dupprintf("Disconnection message text: %.*s", + msglen, msg); logevent(buf); - bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"", + bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"", reason, (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ? ssh2_disconnect_reasons[reason] : "unknown", - buf+nowlen)); + msglen, msg)); sfree(buf); } @@ -8629,7 +9035,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->send_ok = 0; ssh->editing = 0; ssh->echoing = 0; - ssh->v1_throttle_count = 0; + ssh->conn_throttle_count = 0; ssh->overall_bufsize = 0; ssh->fallback_cmd = 0; @@ -8729,6 +9135,7 @@ static void ssh_free(void *handle) sfree(ssh->do_ssh2_authconn_state); sfree(ssh->v_c); sfree(ssh->v_s); + sfree(ssh->fullhostname); if (ssh->crcda_ctx) { crcda_free_context(ssh->crcda_ctx); ssh->crcda_ctx = NULL; @@ -8906,7 +9313,7 @@ static const struct telnet_special *ssh_get_specials(void *handle) static const struct telnet_special ssh2_session_specials[] = { {NULL, TS_SEP}, {"Break", TS_BRK}, - /* These are the signal names defined by draft-ietf-secsh-connect-23. + /* These are the signal names defined by RFC 4254. * They include all the ISO C signals, but are a subset of the POSIX * required signals. */ {"SIGINT (Interrupt)", TS_SIGINT}, @@ -9055,17 +9462,13 @@ void *new_sock_channel(void *handle, Socket s) Ssh ssh = (Ssh) handle; struct ssh_channel *c; c = snew(struct ssh_channel); - c->ssh = ssh; - if (c) { - c->halfopen = TRUE; - c->localid = alloc_channel_id(ssh); - c->closes = 0; - c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ - c->u.pfd.s = s; - bufchain_init(&c->v.v2.outbuffer); - add234(ssh->channels, c); - } + c->ssh = ssh; + ssh2_channel_init(c); + c->halfopen = TRUE; + c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */ + c->u.pfd.s = s; + add234(ssh->channels, c); return c; } @@ -9076,14 +9479,27 @@ void *new_sock_channel(void *handle, Socket s) static void ssh_unthrottle(void *handle, int bufsize) { Ssh ssh = (Ssh) handle; + int buflimit; + if (ssh->version == 1) { if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { ssh->v1_stdout_throttling = 0; - ssh1_throttle(ssh, -1); + ssh_throttle_conn(ssh, -1); } } else { - ssh2_set_window(ssh->mainchan, - ssh->mainchan->v.v2.locmaxwin - bufsize); + if (ssh->mainchan) { + ssh2_set_window(ssh->mainchan, + bufsize < ssh->mainchan->v.v2.locmaxwin ? + ssh->mainchan->v.v2.locmaxwin - bufsize : 0); + if (ssh->cfg.ssh_simple) + buflimit = 0; + else + buflimit = ssh->mainchan->v.v2.locmaxwin; + if (ssh->mainchan->throttling_conn && bufsize <= buflimit) { + ssh->mainchan->throttling_conn = 0; + ssh_throttle_conn(ssh, -1); + } + } } } @@ -9106,7 +9522,6 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); - c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname);