#include "putty.h"
#include "tree234.h"
#include "ssh.h"
+#ifndef NO_GSSAPI
+#include "sshgssc.h"
+#include "sshgss.h"
+#endif
#ifndef FALSE
#define FALSE 0
#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
SSH2_PKTCTX_NOAUTH,
SSH2_PKTCTX_PUBLICKEY,
SSH2_PKTCTX_PASSWORD,
+ SSH2_PKTCTX_GSSAPI,
SSH2_PKTCTX_KBDINTER
} Pkt_ACtx;
#define BUG_SSH2_REKEY 64
#define BUG_SSH2_PK_SESSIONID 128
#define BUG_SSH2_MAXPKT 256
+#define BUG_CHOKES_ON_SSH2_IGNORE 512
/*
* Codes for terminal modes.
}
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);
*
* - OUR_V2_BIGWIN is the window size we advertise for the only
* channel in a simple connection. It must be <= INT_MAX.
+ *
+ * - OUR_V2_MAXPKT is the official "maximum packet size" we send
+ * to the remote side. This actually has nothing to do with the
+ * size of the _packet_, but is instead a limit on the amount
+ * of data we're willing to receive in a single SSH2 channel
+ * data message.
+ *
+ * - OUR_V2_PACKETLIMIT is actually the maximum size of SSH
+ * _packet_ we're prepared to cope with. It must be a multiple
+ * of the cipher block size, and must be at least 35000.
*/
#define SSH1_BUFFER_LIMIT 32768
#define OUR_V2_WINSIZE 16384
#define OUR_V2_BIGWIN 0x7fffffff
#define OUR_V2_MAXPKT 0x4000UL
+#define OUR_V2_PACKETLIMIT 0x9000UL
/* Maximum length of passwords/passphrases (arbitrary) */
#define SSH_MAX_PASSWORD_LEN 100
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
* A channel is completely finished with when all four bits are set.
*/
int closes;
+
+ /*
+ * This flag indicates that a close is pending on the outgoing
+ * side of the channel: that is, wherever we're getting the data
+ * for this channel has sent us some data followed by EOF. We
+ * can't actually close the channel until we've finished sending
+ * the data, so we set this flag instead to remind us to
+ * initiate the closing process once our buffer is clear.
+ */
+ int pending_close;
+
/*
* True if this channel is causing the underlying connection to be
* throttled.
Pkt_KCtx pkt_kctx;
Pkt_ACtx pkt_actx;
- void *x11auth;
+ struct X11Display *x11disp;
int version;
int conn_throttle_count;
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;
+
+#ifndef NO_GSSAPI
+ /*
+ * GSSAPI libraries for this session.
+ */
+ struct ssh_gss_liblist *gsslibs;
+#endif
};
#define logevent(s) logevent(ssh->frontend, s)
PKT_INCOMING, st->pktin->type,
ssh1_pkt_type(st->pktin->type),
st->pktin->body, st->pktin->length,
- nblanks, &blank);
+ nblanks, &blank, NULL);
}
crFinish(st->pktin);
st->cipherblk = 8;
if (st->cipherblk < 8)
st->cipherblk = 8;
+ st->maclen = ssh->scmac ? ssh->scmac->len : 0;
- st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
+ if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) &&
+ ssh->scmac) {
+ /*
+ * When dealing with a CBC-mode cipher, we want to avoid the
+ * possibility of an attacker's tweaking the ciphertext stream
+ * so as to cause us to feed the same block to the block
+ * cipher more than once and thus leak information
+ * (VU#958563). The way we do this is not to take any
+ * decisions on the basis of anything we've decrypted until
+ * we've verified it with a MAC. That includes the packet
+ * length, so we just read data and check the MAC repeatedly,
+ * and when the MAC passes, see if the length we've got is
+ * plausible.
+ */
- /*
- * Acquire and decrypt the first block of the packet. This will
- * contain the length and padding details.
- */
- for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
- while ((*datalen) == 0)
- crReturn(NULL);
- st->pktin->data[st->i] = *(*data)++;
- (*datalen)--;
- }
+ /* May as well allocate the whole lot now. */
+ st->pktin->data = snewn(OUR_V2_PACKETLIMIT + st->maclen + APIEXTRA,
+ unsigned char);
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data, st->cipherblk);
+ /* Read an amount corresponding to the MAC. */
+ for (st->i = 0; st->i < st->maclen; st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->i] = *(*data)++;
+ (*datalen)--;
+ }
- /*
- * Now get the length and padding figures.
- */
- st->len = GET_32BIT(st->pktin->data);
- st->pad = st->pktin->data[4];
+ st->packetlen = 0;
+ {
+ unsigned char seq[4];
+ ssh->scmac->start(ssh->sc_mac_ctx);
+ PUT_32BIT(seq, st->incoming_sequence);
+ ssh->scmac->bytes(ssh->sc_mac_ctx, seq, 4);
+ }
- /*
- * _Completely_ silly lengths should be stomped on before they
- * do us any more damage.
- */
- if (st->len < 0 || st->len > 35000 || st->pad < 4 ||
- st->len - st->pad < 1 || (st->len + 4) % st->cipherblk != 0) {
- bombout(("Incoming packet was garbled on decryption"));
- ssh_free_packet(st->pktin);
- crStop(NULL);
- }
+ for (;;) { /* Once around this loop per cipher block. */
+ /* Read another cipher-block's worth, and tack it onto the end. */
+ for (st->i = 0; st->i < st->cipherblk; st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->packetlen+st->maclen+st->i] = *(*data)++;
+ (*datalen)--;
+ }
+ /* Decrypt one more block (a little further back in the stream). */
+ ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+ st->pktin->data + st->packetlen,
+ st->cipherblk);
+ /* Feed that block to the MAC. */
+ ssh->scmac->bytes(ssh->sc_mac_ctx,
+ st->pktin->data + st->packetlen, st->cipherblk);
+ st->packetlen += st->cipherblk;
+ /* See if that gives us a valid packet. */
+ if (ssh->scmac->verresult(ssh->sc_mac_ctx,
+ st->pktin->data + st->packetlen) &&
+ (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen)
+ break;
+ if (st->packetlen >= OUR_V2_PACKETLIMIT) {
+ bombout(("No valid incoming packet found"));
+ ssh_free_packet(st->pktin);
+ crStop(NULL);
+ }
+ }
+ st->pktin->maxlen = st->packetlen + st->maclen;
+ st->pktin->data = sresize(st->pktin->data,
+ st->pktin->maxlen + APIEXTRA,
+ unsigned char);
+ } else {
+ st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
- /*
- * This enables us to deduce the payload length.
- */
- st->payload = st->len - st->pad - 1;
+ /*
+ * Acquire and decrypt the first block of the packet. This will
+ * contain the length and padding details.
+ */
+ for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->i] = *(*data)++;
+ (*datalen)--;
+ }
- st->pktin->length = st->payload + 5;
+ if (ssh->sccipher)
+ ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+ st->pktin->data, st->cipherblk);
- /*
- * So now we can work out the total packet length.
- */
- st->packetlen = st->len + 4;
- st->maclen = ssh->scmac ? ssh->scmac->len : 0;
+ /*
+ * Now get the length figure.
+ */
+ st->len = GET_32BIT(st->pktin->data);
- /*
- * Allocate memory for the rest of the packet.
- */
- st->pktin->maxlen = st->packetlen + st->maclen;
- st->pktin->data = sresize(st->pktin->data,
- st->pktin->maxlen + APIEXTRA,
- unsigned char);
+ /*
+ * _Completely_ silly lengths should be stomped on before they
+ * do us any more damage.
+ */
+ if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
+ (st->len + 4) % st->cipherblk != 0) {
+ bombout(("Incoming packet was garbled on decryption"));
+ ssh_free_packet(st->pktin);
+ crStop(NULL);
+ }
- /*
- * Read and decrypt the remainder of the packet.
- */
- for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
- st->i++) {
- while ((*datalen) == 0)
- crReturn(NULL);
- st->pktin->data[st->i] = *(*data)++;
- (*datalen)--;
- }
- /* Decrypt everything _except_ the MAC. */
- if (ssh->sccipher)
- ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
- st->pktin->data + st->cipherblk,
- st->packetlen - st->cipherblk);
+ /*
+ * So now we can work out the total packet length.
+ */
+ st->packetlen = st->len + 4;
- st->pktin->encrypted_len = st->packetlen;
+ /*
+ * Allocate memory for the rest of the packet.
+ */
+ st->pktin->maxlen = st->packetlen + st->maclen;
+ st->pktin->data = sresize(st->pktin->data,
+ st->pktin->maxlen + APIEXTRA,
+ unsigned char);
- /*
- * Check the MAC.
- */
- if (ssh->scmac
- && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data, st->len + 4,
- st->incoming_sequence)) {
- bombout(("Incorrect MAC received on packet"));
+ /*
+ * Read and decrypt the remainder of the packet.
+ */
+ for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
+ st->i++) {
+ while ((*datalen) == 0)
+ crReturn(NULL);
+ st->pktin->data[st->i] = *(*data)++;
+ (*datalen)--;
+ }
+ /* Decrypt everything _except_ the MAC. */
+ if (ssh->sccipher)
+ ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
+ st->pktin->data + st->cipherblk,
+ st->packetlen - st->cipherblk);
+
+ /*
+ * Check the MAC.
+ */
+ if (ssh->scmac
+ && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,
+ st->len + 4, st->incoming_sequence)) {
+ bombout(("Incorrect MAC received on packet"));
+ ssh_free_packet(st->pktin);
+ crStop(NULL);
+ }
+ }
+ /* Get and sanity-check the amount of random padding. */
+ st->pad = st->pktin->data[4];
+ if (st->pad < 4 || st->len - st->pad < 1) {
+ bombout(("Invalid padding length on received packet"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
+ /*
+ * This enables us to deduce the payload length.
+ */
+ st->payload = st->len - st->pad - 1;
+
+ st->pktin->length = st->payload + 5;
+ st->pktin->encrypted_len = st->packetlen;
st->pktin->sequence = st->incoming_sequence++;
ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
st->pktin->type),
st->pktin->data+6, st->pktin->length-6,
- nblanks, &blank);
+ nblanks, &blank, &st->pktin->sequence);
}
crFinish(st->pktin);
log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
ssh1_pkt_type(pkt->data[12]),
pkt->body, pkt->length - (pkt->body - pkt->data),
- pkt->nblanks, pkt->blanks);
+ pkt->nblanks, pkt->blanks, NULL);
sfree(pkt->blanks); pkt->blanks = NULL;
pkt->nblanks = 0;
static int s_write(Ssh ssh, void *data, int len)
{
if (ssh->logctx)
- log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
+ log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len,
+ 0, NULL, NULL);
return sk_write(ssh->s, (char *)data, len);
}
log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
pkt->body, pkt->length - (pkt->body - pkt->data),
- pkt->nblanks, pkt->blanks);
+ pkt->nblanks, pkt->blanks, &ssh->v2_outgoing_sequence);
sfree(pkt->blanks); pkt->blanks = NULL;
pkt->nblanks = 0;
{
int len;
if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
- ssh->deferred_len == 0 && !noignore) {
+ ssh->deferred_len == 0 && !noignore &&
+ !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
/*
* Interpose an SSH_MSG_IGNORE to ensure that user data don't
* get encrypted with a known IV.
* unavailable, we don't do this trick at all, because we
* gain nothing by it.)
*/
- if (ssh->cscipher) {
+ if (ssh->cscipher &&
+ !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
int stringlen, i;
stringlen = (256 - ssh->deferred_len);
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) ||
+ 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");
}
+
+ if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+ /*
+ * Servers that don't support SSH2_MSG_IGNORE. Currently,
+ * none detected automatically.
+ */
+ ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
+ logevent("We believe remote version has SSH-2 ignore bug");
+ }
}
/*
/* Log raw data, if we're in that mode. */
if (ssh->logctx)
log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen,
- 0, NULL);
+ 0, NULL, NULL);
crBegin(ssh->ssh_gotdata_crstate);
x11_close(c->u.x11.s);
break;
case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
pfd_close(c->u.pfd.s);
break;
}
del234(ssh->portfwds, pf); /* moving next one to index 0 */
free_portfwd(pf);
}
+ freetree234(ssh->portfwds);
+ ssh->portfwds = NULL;
}
return ret;
SockAddr addr;
const char *err;
- ssh->savedhost = snewn(1 + strlen(host), char);
- strcpy(ssh->savedhost, host);
+ if (*ssh->cfg.loghost) {
+ char *colon;
- if (port < 0)
- port = 22; /* default ssh port */
- ssh->savedport = port;
+ ssh->savedhost = dupstr(ssh->cfg.loghost);
+ ssh->savedport = 22; /* default ssh 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.
sk_addr_free(addr);
return err;
}
+ ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */
/*
* Open socket.
ssh_send_verstring(ssh, NULL);
}
+ /*
+ * loghost, if configured, overrides realhost.
+ */
+ if (*ssh->cfg.loghost) {
+ sfree(*realhost);
+ *realhost = dupstr(ssh->cfg.loghost);
+ }
+
return NULL;
}
fflush(stdout); /* FIXME eh? */
{
- if (!*ssh->cfg.username) {
+ if (!get_remote_username(&ssh->cfg, s->username,
+ sizeof(s->username))) {
int ret; /* need not be kept over crReturn */
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
memcpy(s->username, s->cur_prompt->prompts[0]->result,
lenof(s->username));
free_prompts(s->cur_prompt);
- } else {
- strncpy(s->username, ssh->cfg.username, sizeof(s->username));
- s->username[sizeof(s->username)-1] = '\0';
}
send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);
if (ssh->state == SSH_STATE_CLOSED)
return;
- if (c && !c->closes) {
+ if (!c->closes) {
/*
* If halfopen is true, we have sent
* CHANNEL_OPEN for this channel, but it hasn't even been
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
PKT_END);
+ c->closes = 1; /* sent MSG_CLOSE */
} else {
- struct Packet *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
+ int bytes_to_send = bufchain_size(&c->v.v2.outbuffer);
+ if (bytes_to_send > 0) {
+ /*
+ * If we still have unsent data in our outgoing
+ * buffer for this channel, we can't actually
+ * initiate a close operation yet or that data
+ * will be lost. Instead, set the pending_close
+ * flag so that when we do clear the buffer
+ * we'll start closing the channel.
+ */
+ char logmsg[160] = {'\0'};
+ sprintf(
+ logmsg,
+ "Forwarded port pending to be closed : "
+ "%d bytes remaining",
+ bytes_to_send);
+ logevent(logmsg);
+
+ c->pending_close = TRUE;
+ } else {
+ /*
+ * No locally buffered data, so we can send the
+ * close message immediately.
+ */
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes = 1; /* sent MSG_CLOSE */
+ logevent("Nothing left to send, closing channel");
+ }
}
}
- c->closes = 1; /* sent MSG_CLOSE */
+
if (c->type == CHAN_X11) {
c->u.x11.s = NULL;
logevent("Forwarded X11 connection terminated");
rpf = del234(ssh->rportfwds, pf);
assert(rpf == pf);
+ pf->pfrec->remote = NULL;
free_rportfwd(pf);
}
}
epfrec = add234(ssh->portfwds, pfrec);
if (epfrec != pfrec) {
+ if (epfrec->status == DESTROY) {
+ /*
+ * We already have a port forwarding up and running
+ * with precisely these parameters. Hence, no need
+ * to do anything; simply re-tag the existing one
+ * as KEEP.
+ */
+ epfrec->status = KEEP;
+ }
/*
- * We already have a port forwarding with precisely
- * these parameters. Hence, no need to do anything;
- * simply tag the existing one as KEEP.
+ * Anything else indicates that there was a duplicate
+ * in our input, which we'll silently ignore.
*/
- epfrec->status = KEEP;
free_portfwd(pfrec);
} else {
pfrec->status = CREATE;
logeventf(ssh, "Cancelling %s", message);
sfree(message);
+ /* epf->remote or epf->local may be NULL if setting up a
+ * forwarding failed. */
if (epf->remote) {
struct ssh_rportfwd *rpf = epf->remote;
struct Packet *pktout;
c = snew(struct ssh_channel);
c->ssh = ssh;
- if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
- ssh->x11auth, NULL, -1, &ssh->cfg) != NULL) {
+ if (x11_init(&c->u.x11.s, ssh->x11disp, c,
+ NULL, -1, &ssh->cfg) != NULL) {
logevent("Opening X11 forward connection failed");
sfree(c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_X11; /* identify channel type */
add234(ssh->channels, c);
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_SOCKDATA; /* identify channel type */
add234(ssh->channels, c);
}
}
- if (ssh->cfg.x11_forward) {
- char proto[20], data[64];
+ if (ssh->cfg.x11_forward &&
+ (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
+ ssh->cfg.x11_auth, &ssh->cfg))) {
logevent("Requesting X11 forwarding");
- ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
- data, sizeof(data), ssh->cfg.x11_auth);
- x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
/*
* Note that while we blank the X authentication data here, we don't
* take any special action to blank the start of an X11 channel,
*/
if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, proto,
- PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER,
- PKT_INT, x11_get_screen_number(ssh->cfg.x11_display),
+ PKT_STR, ssh->x11disp->remoteauthprotoname,
+ PKTT_PASSWORD,
+ PKT_STR, ssh->x11disp->remoteauthdatastring,
+ PKTT_OTHER,
+ PKT_INT, ssh->x11disp->screennum,
PKT_END);
} else {
send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
- PKT_STR, proto,
- PKTT_PASSWORD, PKT_STR, data, PKTT_OTHER, PKT_END);
+ PKT_STR, ssh->x11disp->remoteauthprotoname,
+ PKTT_PASSWORD,
+ PKT_STR, ssh->x11disp->remoteauthdatastring,
+ PKTT_OTHER,
+ PKT_END);
}
do {
crReturnV;
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);
}
return bufchain_size(&c->v.v2.outbuffer);
}
-static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
+static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
{
int bufsize;
if (c->closes)
break;
}
}
+
+ /*
+ * If we've emptied the channel's output buffer and there's a
+ * pending close event, start the channel-closing procedure.
+ */
+ if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes = 1;
+ c->pending_close = FALSE;
+ }
}
/*
Ssh ssh = c->ssh;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
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;
return;
if (!c->closes) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
- ssh2_try_send_and_unthrottle(c);
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
* wrap up and close the channel ourselves.
*/
x11_close(c->u.x11.s);
+ c->u.x11.s = NULL;
sshfwd_close(c);
} else if (c->type == CHAN_AGENT) {
sshfwd_close(c);
} else if (c->type == CHAN_SOCKDATA) {
pfd_close(c->u.pfd.s);
+ c->u.pfd.s = NULL;
sshfwd_close(c);
}
}
if (typelen == 3 && !memcmp(type, "x11", 3)) {
char *addrstr;
+ const char *x11err;
ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
addrstr = snewn(peeraddrlen+1, char);
if (!ssh->X11_fwd_enabled)
error = "X11 forwarding is not enabled";
- else if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
- ssh->x11auth, addrstr, peerport,
- &ssh->cfg) != NULL) {
+ else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,
+ addrstr, peerport, &ssh->cfg)) != NULL) {
+ logeventf(ssh, "Local X11 connection failed: %s", x11err);
error = "Unable to open an X11 connection";
} else {
logevent("Opening X11 forward connection succeeded");
}
/*
- * 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);
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;
+#ifndef NO_GSSAPI
+ int can_gssapi;
+ 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];
int try_send;
int num_env, env_left, env_ok;
struct Packet *pktout;
+#ifndef NO_GSSAPI
+ struct ssh_gss_library *gsslib;
+ 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;
+#endif
};
crState(do_ssh2_authconn_state);
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 (!ssh->cfg.ssh_no_userauth) {
/*
* Request userauth protocol, and await a response to it.
* with change_username turned off we don't try to get
* it again.
*/
- } else if (!*ssh->cfg.username) {
+ } else if (!get_remote_username(&ssh->cfg, s->username,
+ sizeof(s->username))) {
int ret; /* need not be kept over crReturn */
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
free_prompts(s->cur_prompt);
} else {
char *stuff;
- strncpy(s->username, ssh->cfg.username, sizeof(s->username));
- s->username[sizeof(s->username)-1] = '\0';
if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
stuff = dupprintf("Using username \"%s\".\r\n", s->username);
c_write_str(ssh, stuff);
}
while (1) {
+ char *methods = NULL;
+ int methlen = 0;
+
/*
* Wait for the result of the last authentication request.
*/
}
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
logevent("Access granted");
- s->we_are_in = TRUE;
+ s->we_are_in = s->userauth_success = TRUE;
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;
* helpfully try next.
*/
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
- char *methods;
- int methlen;
ssh_pkt_getstring(pktin, &methods, &methlen);
if (!ssh2_pkt_getbool(pktin)) {
/*
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
+ 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) &&
+ ssh->gsslibs->nlibraries > 0;
+#endif
}
ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
key->alg->freekey(key->data);
}
+#ifndef NO_GSSAPI
+ } else if (s->can_gssapi && !s->tried_gssapi) {
+
+ /* GSSAPI Authentication */
+
+ int micoffset, len;
+ char *data;
+ Ssh_gss_buf mic;
+ s->type = AUTH_TYPE_GSSAPI;
+ s->tried_gssapi = TRUE;
+ s->gotit = TRUE;
+ ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
+
+ /*
+ * Pick the highest GSS library on the preference
+ * list.
+ */
+ {
+ int i, j;
+ s->gsslib = NULL;
+ for (i = 0; i < ngsslibs; i++) {
+ int want_id = ssh->cfg.ssh_gsslist[i];
+ 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 */
+ }
+ }
+ got_gsslib:
+ /*
+ * We always expect to have found something in
+ * the above loop: we only came here if there
+ * was at least one viable GSS library, and the
+ * preference list should always mention
+ * everything and only change the order.
+ */
+ assert(s->gsslib);
+ }
+
+ if (s->gsslib->gsslogmsg)
+ logevent(s->gsslib->gsslogmsg);
+
+ /* 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 */
+ s->gsslib->indicate_mech(s->gsslib, &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.length + 2);
+ ssh2_pkt_addbyte(s->pktout, SSH2_GSS_OIDTYPE);
+
+ /* length of OID */
+ ssh2_pkt_addbyte(s->pktout, (unsigned char) s->gss_buf.length);
+
+ ssh_pkt_adddata(s->pktout, s->gss_buf.value,
+ s->gss_buf.length);
+ 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, &data, &len);
+ s->gss_rcvtok.value = data;
+ s->gss_rcvtok.length = len;
+ if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
+ ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
+ ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
+ memcmp((char *)s->gss_rcvtok.value + 2,
+ s->gss_buf.value,s->gss_buf.length) ) {
+ logevent("GSSAPI authentication - wrong response from server");
+ continue;
+ }
+
+ /* now start running */
+ s->gss_stat = s->gsslib->import_name(s->gsslib,
+ 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 = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx);
+
+ if (s->gss_stat != SSH_GSS_OK) {
+ logevent("GSSAPI authentication failed to get credentials");
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ continue;
+ }
+
+ /* initial tokens are empty */
+ SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
+ SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
+
+ /* now enter the loop */
+ do {
+ s->gss_stat = s->gsslib->init_sec_context
+ (s->gsslib,
+ &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 (s->gsslib->display_status(s->gsslib, s->gss_ctx,
+ &s->gss_buf) == SSH_GSS_OK) {
+ logevent(s->gss_buf.value);
+ sfree(s->gss_buf.value);
+ }
+
+ break;
+ }
+ logevent("GSSAPI authentication initialised");
+
+ /* Client and server now exchange tokens until GSSAPI
+ * no longer says CONTINUE_NEEDED */
+
+ if (s->gss_sndtok.length != 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.value,s->gss_sndtok.length);
+ ssh2_pkt_send(ssh, s->pktout);
+ s->gsslib->free_tok(s->gsslib, &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, &data, &len);
+ s->gss_rcvtok.value = data;
+ s->gss_rcvtok.length = len;
+ }
+ } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
+
+ if (s->gss_stat != SSH_GSS_OK) {
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ s->gsslib->release_cred(s->gsslib, &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.value = (char *)s->pktout->data + micoffset;
+ s->gss_buf.length = s->pktout->length - micoffset;
+
+ s->gsslib->get_mic(s->gsslib, 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.value, mic.length);
+ ssh2_pkt_send(ssh, s->pktout);
+ s->gsslib->free_mic(s->gsslib, &mic);
+
+ s->gotit = FALSE;
+
+ s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+ s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
+ continue;
+#endif
} else if (s->can_keyb_inter && !s->kbd_inter_refused) {
/*
ssh_pkt_getstring(pktin, &lang, &lang_len);
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
- if (name_len) {
- /* FIXME: better prefix to distinguish from
- * local prompts? */
- s->cur_prompt->name =
- dupprintf("SSH server: %.*s", name_len, name);
- s->cur_prompt->name_reqd = TRUE;
- } else {
- s->cur_prompt->name =
- dupstr("SSH server authentication");
- s->cur_prompt->name_reqd = FALSE;
- }
- /* FIXME: ugly to print "Using..." in prompt _every_
- * time round. Can this be done more subtly? */
- s->cur_prompt->instruction =
- dupprintf("Using keyboard-interactive authentication.%s%.*s",
- inst_len ? "\n" : "", inst_len, inst);
- s->cur_prompt->instr_reqd = TRUE;
/*
- * Get the prompts from the packet.
+ * Get any prompt(s) from the packet.
*/
s->num_prompts = ssh_pkt_getuint32(pktin);
for (i = 0; i < s->num_prompts; i++) {
echo, SSH_MAX_PASSWORD_LEN);
}
+ if (name_len) {
+ /* FIXME: better prefix to distinguish from
+ * local prompts? */
+ s->cur_prompt->name =
+ dupprintf("SSH server: %.*s", name_len, name);
+ s->cur_prompt->name_reqd = TRUE;
+ } else {
+ s->cur_prompt->name =
+ dupstr("SSH server authentication");
+ s->cur_prompt->name_reqd = FALSE;
+ }
+ /* We add a prefix to try to make it clear that a prompt
+ * has come from the server.
+ * FIXME: ugly to print "Using..." in prompt _every_
+ * time round. Can this be done more subtly? */
+ /* Special case: for reasons best known to themselves,
+ * some servers send k-i requests with no prompts and
+ * nothing to display. Keep quiet in this case. */
+ if (s->num_prompts || name_len || inst_len) {
+ s->cur_prompt->instruction =
+ dupprintf("Using keyboard-interactive authentication.%s%.*s",
+ inst_len ? "\n" : "", inst_len, inst);
+ s->cur_prompt->instr_reqd = TRUE;
+ } else {
+ s->cur_prompt->instr_reqd = FALSE;
+ }
+
/*
- * Get the user's responses.
+ * Display any instructions, and get the user's
+ * response(s).
*/
- if (s->num_prompts) {
+ {
int ret; /* not live over crReturn */
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
}
/*
- * Send the responses to the server.
+ * Send the response(s) to the server.
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
ssh2_pkt_adduint32(s->pktout, s->num_prompts);
sfree(s->password);
} else {
+ char *str = dupprintf("No supported authentication methods available"
+ " (server sent: %.*s)",
+ methlen, methods);
- ssh_disconnect(ssh, NULL,
+ ssh_disconnect(ssh, str,
"No supported authentication methods available",
SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
FALSE);
+ sfree(str);
+
crStopV;
}
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->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =
ssh2_msg_channel_open;
- if (ssh->cfg.ssh_simple) {
+ if (ssh->mainchan && ssh->cfg.ssh_simple) {
/*
* This message indicates to the server that we promise
* not to try to run any other channel in parallel with
/*
* Potentially enable X11 forwarding.
*/
- if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
- char proto[20], data[64];
+ if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward &&
+ (ssh->x11disp = x11_setup_display(ssh->cfg.x11_display,
+ ssh->cfg.x11_auth, &ssh->cfg))) {
logevent("Requesting X11 forwarding");
- ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
- data, sizeof(data), ssh->cfg.x11_auth);
- x11_get_real_auth(ssh->x11auth, ssh->cfg.x11_display);
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
ssh2_pkt_addstring(s->pktout, "x11-req");
ssh2_pkt_addbool(s->pktout, 1); /* want reply */
ssh2_pkt_addbool(s->pktout, 0); /* many connections */
- ssh2_pkt_addstring(s->pktout, proto);
+ ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthprotoname);
/*
* Note that while we blank the X authentication data here, we don't
* take any special action to blank the start of an X11 channel,
* cookie into the log.
*/
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
- ssh2_pkt_addstring(s->pktout, data);
+ ssh2_pkt_addstring(s->pktout, ssh->x11disp->remoteauthdatastring);
end_log_omission(ssh, s->pktout);
- ssh2_pkt_adduint32(s->pktout, x11_get_screen_number(ssh->cfg.x11_display));
+ ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum);
ssh2_pkt_send(ssh, s->pktout);
crWaitUntilV(pktin);
* Try to send data on all channels if we can.
*/
for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
- ssh2_try_send_and_unthrottle(c);
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
{
/* 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);
}
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);
}
ssh->fallback_cmd = 0;
ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
- ssh->x11auth = NULL;
+ ssh->x11disp = NULL;
ssh->v1_compressing = FALSE;
ssh->v2_outgoing_sequence = 0;
ssh->ssh1_rdpkt_crstate = 0;
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->rportfwds) {
while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
- sfree(pf);
+ free_rportfwd(pf);
freetree234(ssh->rportfwds);
ssh->rportfwds = NULL;
}
sfree(ssh->deferred_send_data);
- if (ssh->x11auth)
- x11_free_auth(ssh->x11auth);
+ if (ssh->x11disp)
+ x11_free_display(ssh->x11disp);
sfree(ssh->do_ssh_init_state);
sfree(ssh->do_ssh1_login_state);
sfree(ssh->do_ssh2_transport_state);
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;
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();
static const struct telnet_special ssh1_ignore_special[] = {
{"IGNORE message", TS_NOP}
};
- static const struct telnet_special ssh2_transport_specials[] = {
+ static const struct telnet_special ssh2_ignore_special[] = {
{"IGNORE message", TS_NOP},
+ };
+ static const struct telnet_special ssh2_rekey_special[] = {
{"Repeat key exchange", TS_REKEY},
};
static const struct telnet_special ssh2_session_specials[] = {
{NULL, TS_EXITMENU}
};
/* XXX review this length for any changes: */
- static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) +
+ static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) +
+ lenof(ssh2_rekey_special) +
lenof(ssh2_session_specials) +
lenof(specials_end)];
Ssh ssh = (Ssh) handle;
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
ADD_SPECIALS(ssh1_ignore_special);
} else if (ssh->version == 2) {
- ADD_SPECIALS(ssh2_transport_specials);
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))
+ ADD_SPECIALS(ssh2_ignore_special);
+ if (!(ssh->remote_bugs & BUG_SSH2_REKEY))
+ ADD_SPECIALS(ssh2_rekey_special);
if (ssh->mainchan)
ADD_SPECIALS(ssh2_session_specials);
} /* else we're not ready yet */
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
} else {
- pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
- ssh2_pkt_addstring_start(pktout);
- ssh2_pkt_send_noqueue(ssh, pktout);
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+ pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start(pktout);
+ ssh2_pkt_send_noqueue(ssh, pktout);
+ }
}
} else if (code == TS_REKEY) {
if (!ssh->kex_in_progress && ssh->version == 2) {