+/*
+ * SSH backend.
+ */
+
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
+#include <limits.h>
+#include <signal.h>
#include "putty.h"
#include "tree234.h"
void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
const struct ssh_signkey *hostkey;
- unsigned char v2_session_id[32];
+ unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];
int v2_session_id_len;
void *kex_ctx;
tree234 *channels; /* indexed by local id */
struct ssh_channel *mainchan; /* primary session channel */
+ int ncmode; /* is primary channel direct-tcpip? */
int exitcode;
int close_expected;
int clean_exit;
{
int i;
for (i = 0; i < len; i++)
- if (buf[i] != '\r' && (trusted || buf[i] & 0x60))
+ if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))
fputc(buf[i], stderr);
}
return biglen + 4; /* len(length+padding+type+data+CRC) */
}
+static int s_write(Ssh ssh, void *data, int len)
+{
+ log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
+ return sk_write(ssh->s, (char *)data, len);
+}
+
static void s_wrpkt(Ssh ssh, struct Packet *pkt)
{
int len, backlog, offset;
len = s_wrpkt_prepare(ssh, pkt, &offset);
- backlog = sk_write(ssh->s, (char *)pkt->data + offset, len);
+ backlog = s_write(ssh, pkt->data + offset, len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
ssh_free_packet(pkt);
while ((argtype = va_arg(ap, int)) != PKT_END) {
unsigned char *argp, argchar;
+ char *sargp;
unsigned long argint;
int arglen;
switch (argtype) {
ssh_pkt_adddata(pkt, argp, arglen);
break;
case PKT_STR:
- argp = va_arg(ap, unsigned char *);
- ssh_pkt_addstring(pkt, argp);
+ sargp = va_arg(ap, char *);
+ ssh_pkt_addstring(pkt, sargp);
break;
case PKT_BIGNUM:
bn = va_arg(ap, Bignum);
static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b)
{
int len = ssh1_bignum_length(b);
- unsigned char *data = snewn(len, char);
+ unsigned char *data = snewn(len, unsigned char);
(void) ssh1_write_bignum(data, b);
ssh_pkt_adddata(pkt, data, len);
sfree(data);
return;
}
len = ssh2_pkt_construct(ssh, pkt);
- backlog = sk_write(ssh->s, (char *)pkt->data, len);
+ backlog = s_write(ssh, pkt->data, len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
* get encrypted with a known IV.
*/
struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start(ipkt);
ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
}
len = ssh2_pkt_construct(ssh, pkt);
ssh2_pkt_send_noqueue(ssh, pkt);
}
-#if 0 /* disused */
/*
* Either queue or defer a packet, depending on whether queueing is
* set.
else
ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
}
-#endif
/*
* Send the whole deferred data block constructed by
static void ssh_pkt_defersend(Ssh ssh)
{
int backlog;
- backlog = sk_write(ssh->s, (char *)ssh->deferred_send_data,
- ssh->deferred_len);
+ backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len);
ssh->deferred_len = ssh->deferred_size = 0;
sfree(ssh->deferred_send_data);
ssh->deferred_send_data = NULL;
}
/*
+ * Send a packet whose length needs to be disguised (typically
+ * passwords or keyboard-interactive responses).
+ */
+static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
+ int padsize)
+{
+#if 0
+ if (0) {
+ /*
+ * The simplest way to do this is to adjust the
+ * variable-length padding field in the outgoing packet.
+ *
+ * Currently compiled out, because some Cisco SSH servers
+ * don't like excessively padded packets (bah, why's it
+ * always Cisco?)
+ */
+ pkt->forcepad = padsize;
+ ssh2_pkt_send(ssh, pkt);
+ } else
+#endif
+ {
+ /*
+ * If we can't do that, however, an alternative approach is
+ * to use the pkt_defer mechanism to bundle the packet
+ * tightly together with an SSH_MSG_IGNORE such that their
+ * combined length is a constant. So first we construct the
+ * final form of this packet and defer its sending.
+ */
+ ssh2_pkt_defer(ssh, pkt);
+
+ /*
+ * Now construct an SSH_MSG_IGNORE which includes a string
+ * that's an exact multiple of the cipher block size. (If
+ * the cipher is NULL so that the block size is
+ * unavailable, we don't do this trick at all, because we
+ * gain nothing by it.)
+ */
+ if (ssh->cscipher) {
+ int stringlen, i;
+
+ stringlen = (256 - ssh->deferred_len);
+ stringlen += ssh->cscipher->blksize - 1;
+ stringlen -= (stringlen % ssh->cscipher->blksize);
+ if (ssh->cscomp) {
+ /*
+ * Temporarily disable actual compression, so we
+ * can guarantee to get this string exactly the
+ * length we want it. The compression-disabling
+ * routine should return an integer indicating how
+ * many bytes we should adjust our string length
+ * by.
+ */
+ stringlen -=
+ ssh->cscomp->disable_compression(ssh->cs_comp_ctx);
+ }
+ pkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start(pkt);
+ for (i = 0; i < stringlen; i++) {
+ char c = (char) random_byte();
+ ssh2_pkt_addstring_data(pkt, &c, 1);
+ }
+ ssh2_pkt_defer(ssh, pkt);
+ }
+ ssh_pkt_defersend(ssh);
+ }
+}
+
+/*
* Send all queued SSH-2 packets. We send them by means of
* ssh2_pkt_defer_noqueue(), in case they included a pair of
* packets that needed to be lumped together.
ssh->remote_bugs = 0;
+ /*
+ * General notes on server version strings:
+ * - Not all servers reporting "Cisco-1.25" have all the bugs listed
+ * here -- in particular, we've heard of one that's perfectly happy
+ * with SSH1_MSG_IGNOREs -- but this string never seems to change,
+ * so we can't distinguish them.
+ */
if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
(ssh->cfg.sshbug_ignore1 == AUTO &&
(!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
}
logeventf(ssh, "We claim version: %.*s",
strcspn(verstring, "\015\012"), verstring);
- sk_write(ssh->s, verstring, strlen(verstring));
+ s_write(ssh, verstring, strlen(verstring));
sfree(verstring);
if (ssh->version == 2)
do_ssh2_transport(ssh, NULL, -1, NULL);
static void ssh_process_incoming_data(Ssh ssh,
unsigned char **data, int *datalen)
{
- struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
+ struct Packet *pktin;
+
+ pktin = ssh->s_rdpkt(ssh, data, datalen);
if (pktin) {
ssh->protocol(ssh, NULL, 0, pktin);
ssh_free_packet(pktin);
static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{
+ /* Log raw data, if we're in that mode. */
+ log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL);
+
crBegin(ssh->ssh_gotdata_crstate);
/*
error_msg = "Server closed network connection";
}
+ if (ssh->close_expected && ssh->clean_exit && ssh->exitcode < 0)
+ ssh->exitcode = 0;
+
if (need_notify)
notify_remote_exit(ssh->frontend);
while (pktin->type == SSH1_SMSG_FAILURE) {
s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
- if (agent_exists() && !s->tried_agent) {
+ if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) {
/*
* Attempt RSA authentication using Pageant.
*/
s->p += 4;
logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
- logeventf(ssh, "Trying Pageant key #%d", s->keyi);
- if (s->publickey_blob &&
- !memcmp(s->p, s->publickey_blob,
- s->publickey_bloblen)) {
- logevent("This key matches configured key file");
- s->tried_publickey = 1;
- }
+ unsigned char *pkblob = s->p;
s->p += 4;
{
int n, ok = FALSE;
break;
}
}
+ if (s->publickey_blob) {
+ if (!memcmp(pkblob, s->publickey_blob,
+ s->publickey_bloblen)) {
+ logeventf(ssh, "Pageant key #%d matches "
+ "configured key file", s->keyi);
+ s->tried_publickey = 1;
+ } else
+ /* Skip non-configured key */
+ continue;
+ }
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
send_packet(ssh, SSH1_CMSG_AUTH_RSA,
PKT_BIGNUM, s->key.modulus, PKT_END);
crWaitUntil(pktin);
break;
}
sfree(s->response);
+ if (s->publickey_blob && !s->tried_publickey)
+ logevent("Configured key file not in Pageant");
}
if (s->authed)
break;
/* and try again */
} else {
assert(0 && "unexpected return from loadrsakey()");
+ got_passphrase = FALSE; /* placate optimisers */
}
}
* magnitude of the password length, but it will
* introduce a bit of extra uncertainty.
*
- * A few servers (the old 1.2.18 through 1.2.22)
- * can't deal with SSH1_MSG_IGNORE. For these
- * servers, we need an alternative defence. We make
- * use of the fact that the password is interpreted
- * as a C string: so we can append a NUL, then some
- * random data.
+ * A few servers can't deal with SSH1_MSG_IGNORE, at
+ * least in this context. For these servers, we need
+ * an alternative defence. We make use of the fact
+ * that the password is interpreted as a C string:
+ * so we can append a NUL, then some random data.
*
- * One server (a Cisco one) can deal with neither
- * SSH1_MSG_IGNORE _nor_ a padded password string.
- * For this server we are left with no defences
+ * A few servers can deal with neither SSH1_MSG_IGNORE
+ * here _nor_ a padded password string.
+ * For these servers we are left with no defences
* against password length sniffing.
*/
- if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) &&
+ !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
/*
* The server can deal with SSH1_MSG_IGNORE, so
* we can use the primary defence.
PKTT_OTHER, PKT_END);
} else {
/*
- * The server has _both_
- * BUG_CHOKES_ON_SSH1_IGNORE and
- * BUG_NEEDS_SSH1_PLAIN_PASSWORD. There is
- * therefore nothing we can do.
+ * The server is believed unable to cope with
+ * any of our password camouflage methods.
*/
int len;
len = strlen(s->cur_prompt->prompts[0]->result);
/* Data for an agent message. Buffer it. */
while (len > 0) {
if (c->u.a.lensofar < 4) {
- unsigned int l = min(4 - c->u.a.lensofar, len);
+ unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len);
memcpy(c->u.a.msglen + c->u.a.lensofar, p,
l);
p += l;
if (c->u.a.lensofar >= 4 && len > 0) {
unsigned int l =
min(c->u.a.totallen - c->u.a.lensofar,
- len);
+ (unsigned)len);
memcpy(c->u.a.message + c->u.a.lensofar, p,
l);
p += l;
/*
* SSH-2 key creation method.
+ * (Currently assumes 2 lots of any hash are sufficient to generate
+ * keys/IVs for any cipher/MAC. SSH2_MKKEY_ITERS documents this assumption.)
*/
+#define SSH2_MKKEY_ITERS (2)
static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,
unsigned char *keyspace)
{
char *hostkeydata, *sigdata, *keystr, *fingerprint;
int hostkeylen, siglen;
void *hkey; /* actual host key */
- unsigned char exchange_hash[32];
+ unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
int n_preferred_kex;
const struct ssh_kexes *preferred_kex[KEX_MAX];
int n_preferred_ciphers;
s->preferred_kex[s->n_preferred_kex++] =
&ssh_diffiehellman_group1;
break;
- case CIPHER_WARN:
+ case KEX_WARN:
/* Flag for later. Don't bother if it's the last in
* the list. */
if (i < KEX_MAX - 1) {
ssh->kex->groupname);
}
- logevent("Doing Diffie-Hellman key exchange");
+ logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
+ ssh->kex->hash->text_name);
/*
* Now generate and send e for Diffie-Hellman.
*/
* hash from the _first_ key exchange.
*/
{
- unsigned char keyspace[40];
+ unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];
+ assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace);
+ assert((ssh->cscipher->keylen+7) / 8 <=
+ ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace);
+ assert(ssh->cscipher->blksize <=
+ ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace);
+ assert(ssh->csmac->len <=
+ ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
+ memset(keyspace, 0, sizeof(keyspace));
}
logeventf(ssh, "Initialised %.200s client->server encryption",
* hash from the _first_ key exchange.
*/
{
- unsigned char keyspace[40];
+ unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];
+ assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace);
+ assert((ssh->sccipher->keylen+7) / 8 <=
+ ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace);
+ assert(ssh->sccipher->blksize <=
+ ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);
ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace);
+ assert(ssh->scmac->len <=
+ ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
+ memset(keyspace, 0, sizeof(keyspace));
}
logeventf(ssh, "Initialised %.200s server->client encryption",
ssh->sccipher->text_name);
case CHAN_AGENT:
while (length > 0) {
if (c->u.a.lensofar < 4) {
- unsigned int l = min(4 - c->u.a.lensofar, length);
+ unsigned int l = min(4 - c->u.a.lensofar,
+ (unsigned)length);
memcpy(c->u.a.msglen + c->u.a.lensofar,
data, l);
data += l;
if (c->u.a.lensofar >= 4 && length > 0) {
unsigned int l =
min(c->u.a.totallen - c->u.a.lensofar,
- length);
+ (unsigned)length);
memcpy(c->u.a.message + c->u.a.lensofar,
data, l);
data += l;
is_plausible = FALSE;
}
}
+ ssh->exitcode = 128; /* means `unknown signal' */
if (is_plausible) {
if (is_int) {
/* Old non-standard OpenSSH. */
int signum = ssh_pkt_getuint32(pktin);
fmt_sig = dupprintf(" %d", signum);
+ ssh->exitcode = 128 + signum;
} else {
/* As per the drafts. */
char *sig;
fmt_sig = dupprintf(" \"%.*s\"",
siglen, sig);
}
+
+ /*
+ * Really hideous method of translating the
+ * signal description back into a locally
+ * meaningful number.
+ */
+
+ if (0)
+ ;
+#define TRANSLATE_SIGNAL(s) \
+ else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \
+ ssh->exitcode = 128 + SIG ## s
+#ifdef SIGABRT
+ TRANSLATE_SIGNAL(ABRT);
+#endif
+#ifdef SIGALRM
+ TRANSLATE_SIGNAL(ALRM);
+#endif
+#ifdef SIGFPE
+ TRANSLATE_SIGNAL(FPE);
+#endif
+#ifdef SIGHUP
+ TRANSLATE_SIGNAL(HUP);
+#endif
+#ifdef SIGILL
+ TRANSLATE_SIGNAL(ILL);
+#endif
+#ifdef SIGINT
+ TRANSLATE_SIGNAL(INT);
+#endif
+#ifdef SIGKILL
+ TRANSLATE_SIGNAL(KILL);
+#endif
+#ifdef SIGPIPE
+ TRANSLATE_SIGNAL(PIPE);
+#endif
+#ifdef SIGQUIT
+ TRANSLATE_SIGNAL(QUIT);
+#endif
+#ifdef SIGSEGV
+ TRANSLATE_SIGNAL(SEGV);
+#endif
+#ifdef SIGTERM
+ TRANSLATE_SIGNAL(TERM);
+#endif
+#ifdef SIGUSR1
+ TRANSLATE_SIGNAL(USR1);
+#endif
+#ifdef SIGUSR2
+ TRANSLATE_SIGNAL(USR2);
+#endif
+#undef TRANSLATE_SIGNAL
+ else
+ ssh->exitcode = 128;
}
core = ssh2_pkt_getbool(pktin);
ssh_pkt_getstring(pktin, &msg, &msglen);
} type;
int done_service_req;
int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
- int tried_pubkey_config, tried_agent;
+ int tried_pubkey_config, done_agent;
int kbd_inter_refused;
int we_are_in;
prompts_t *cur_prompt;
int publickey_encrypted;
char *publickey_algorithm;
char *publickey_comment;
- unsigned char request[5], *response, *p;
- int responselen;
+ unsigned char agent_request[5], *agent_response, *agentp;
+ int agent_responselen;
+ unsigned char *pkblob_in_agent;
int keyi, nkeys;
- int authed;
char *pkblob, *alg, *commentp;
int pklen, alglen, commentlen;
int siglen, retlen, len;
}
}
+ /* Arrange to be able to deal with any BANNERs that come in.
+ * (We do this now as packets may come in during the next bit.) */
+ bufchain_init(&ssh->banner);
+ ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =
+ ssh2_msg_userauth_banner;
+
/*
* Misc one-time setup for authentication.
*/
}
}
+ /*
+ * Find out about any keys Pageant has (but if there's a
+ * public key configured, filter out all others).
+ */
+ s->nkeys = 0;
+ s->agent_response = NULL;
+ s->pkblob_in_agent = NULL;
+ if (ssh->cfg.tryagent && agent_exists()) {
+
+ void *r;
+
+ logevent("Pageant is running. Requesting keys.");
+
+ /* Request the keys held by the agent. */
+ PUT_32BIT(s->agent_request, 1);
+ s->agent_request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
+ if (!agent_query(s->agent_request, 5, &r, &s->agent_responselen,
+ ssh_agent_callback, ssh)) {
+ do {
+ crReturnV;
+ if (pktin) {
+ bombout(("Unexpected data from server while"
+ " waiting for agent response"));
+ crStopV;
+ }
+ } while (pktin || inlen > 0);
+ r = ssh->agent_response;
+ s->agent_responselen = ssh->agent_response_len;
+ }
+ s->agent_response = (unsigned char *) r;
+ if (s->agent_response && s->agent_responselen >= 5 &&
+ s->agent_response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
+ int keyi;
+ unsigned char *p;
+ p = s->agent_response + 5;
+ s->nkeys = GET_32BIT(p);
+ p += 4;
+ logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
+ if (s->publickey_blob) {
+ /* See if configured key is in agent. */
+ for (keyi = 0; keyi < s->nkeys; keyi++) {
+ s->pklen = GET_32BIT(p);
+ if (s->pklen == s->publickey_bloblen &&
+ !memcmp(p+4, s->publickey_blob,
+ s->publickey_bloblen)) {
+ logeventf(ssh, "Pageant key #%d matches "
+ "configured key file", keyi);
+ s->keyi = keyi;
+ s->pkblob_in_agent = p;
+ break;
+ }
+ p += 4 + s->pklen;
+ p += GET_32BIT(p) + 4; /* comment */
+ }
+ if (!s->pkblob_in_agent) {
+ logevent("Configured key file not in Pageant");
+ s->nkeys = 0;
+ }
+ }
+ }
+ }
+
}
/*
*/
s->username[0] = '\0';
s->got_username = FALSE;
- bufchain_init(&ssh->banner);
- ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =
- ssh2_msg_userauth_banner;
while (!s->we_are_in) {
/*
* Get a username.
s->we_are_in = FALSE;
s->tried_pubkey_config = FALSE;
- s->tried_agent = FALSE;
s->kbd_inter_refused = FALSE;
+ /* Reset agent request state. */
+ s->done_agent = FALSE;
+ if (s->agent_response) {
+ if (s->pkblob_in_agent) {
+ s->agentp = s->pkblob_in_agent;
+ } else {
+ s->agentp = s->agent_response + 5 + 4;
+ s->keyi = 0;
+ }
+ }
+
while (1) {
/*
* Wait for the result of the last authentication request.
ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
- if (s->can_pubkey && agent_exists() && !s->tried_agent) {
+ if (s->can_pubkey && !s->done_agent && s->nkeys) {
/*
- * Attempt public-key authentication using Pageant.
+ * Attempt public-key authentication using a key from Pageant.
*/
- void *r;
- s->authed = FALSE;
ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
- s->tried_agent = TRUE;
+ logeventf(ssh, "Trying Pageant key #%d", s->keyi);
+
+ /* Unpack key from agent response */
+ s->pklen = GET_32BIT(s->agentp);
+ s->agentp += 4;
+ s->pkblob = (char *)s->agentp;
+ s->agentp += s->pklen;
+ s->alglen = GET_32BIT(s->pkblob);
+ s->alg = s->pkblob + 4;
+ s->commentlen = GET_32BIT(s->agentp);
+ s->agentp += 4;
+ s->commentp = (char *)s->agentp;
+ s->agentp += s->commentlen;
+ /* s->agentp now points at next key, if any */
+
+ /* See if server will accept it */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ /* service requested */
+ ssh2_pkt_addstring(s->pktout, "publickey");
+ /* method */
+ ssh2_pkt_addbool(s->pktout, FALSE); /* no signature included */
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
+ ssh2_pkt_send(ssh, s->pktout);
+ s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
- logevent("Pageant is running. Requesting keys.");
+ /* Offer of key refused. */
+ s->gotit = TRUE;
- /* Request the keys held by the agent. */
- PUT_32BIT(s->request, 1);
- s->request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
- if (!agent_query(s->request, 5, &r, &s->responselen,
- ssh_agent_callback, ssh)) {
- do {
- crReturnV;
- if (pktin) {
- bombout(("Unexpected data from server while"
- " waiting for agent response"));
- crStopV;
- }
- } while (pktin || inlen > 0);
- r = ssh->agent_response;
- s->responselen = ssh->agent_response_len;
- }
- s->response = (unsigned char *) r;
- if (s->response && s->responselen >= 5 &&
- s->response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
- s->p = s->response + 5;
- s->nkeys = GET_32BIT(s->p);
- s->p += 4;
- logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
- for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
- void *vret;
+ } else {
+
+ void *vret;
- logeventf(ssh, "Trying Pageant key #%d", s->keyi);
- s->pklen = GET_32BIT(s->p);
- s->p += 4;
- if (s->publickey_blob &&
- s->pklen == s->publickey_bloblen &&
- !memcmp(s->p, s->publickey_blob,
- s->publickey_bloblen)) {
- logevent("This key matches configured key file");
- s->tried_pubkey_config = 1;
- }
- s->pkblob = (char *)s->p;
- s->p += s->pklen;
- s->alglen = GET_32BIT(s->pkblob);
- s->alg = s->pkblob + 4;
- s->commentlen = GET_32BIT(s->p);
- s->p += 4;
- s->commentp = (char *)s->p;
- s->p += s->commentlen;
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(s->pktout, s->username);
- ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */
- ssh2_pkt_addstring(s->pktout, "publickey"); /* method */
- ssh2_pkt_addbool(s->pktout, FALSE); /* no signature included */
- ssh2_pkt_addstring_start(s->pktout);
- ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);
- ssh2_pkt_addstring_start(s->pktout);
- ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
- ssh2_pkt_send(ssh, s->pktout);
-
- crWaitUntilV(pktin);
- if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
- logevent("Key refused");
- continue;
- }
+ if (flags & FLAG_VERBOSE) {
+ c_write_str(ssh, "Authenticating with "
+ "public key \"");
+ c_write(ssh, s->commentp, s->commentlen);
+ c_write_str(ssh, "\" from agent\r\n");
+ }
- if (flags & FLAG_VERBOSE) {
- c_write_str(ssh, "Authenticating with "
- "public key \"");
- c_write(ssh, s->commentp, s->commentlen);
- c_write_str(ssh, "\" from agent\r\n");
- }
+ /*
+ * Server is willing to accept the key.
+ * Construct a SIGN_REQUEST.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(s->pktout, s->username);
+ ssh2_pkt_addstring(s->pktout, "ssh-connection");
+ /* service requested */
+ ssh2_pkt_addstring(s->pktout, "publickey");
+ /* method */
+ ssh2_pkt_addbool(s->pktout, TRUE); /* signature included */
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
- /*
- * Server is willing to accept the key.
- * Construct a SIGN_REQUEST.
- */
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(s->pktout, s->username);
- ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */
- ssh2_pkt_addstring(s->pktout, "publickey"); /* method */
- ssh2_pkt_addbool(s->pktout, TRUE);
- ssh2_pkt_addstring_start(s->pktout);
- ssh2_pkt_addstring_data(s->pktout, s->alg, s->alglen);
- ssh2_pkt_addstring_start(s->pktout);
- ssh2_pkt_addstring_data(s->pktout, s->pkblob, s->pklen);
-
- s->siglen = s->pktout->length - 5 + 4 +
- ssh->v2_session_id_len;
- if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
- s->siglen -= 4;
- s->len = 1; /* message type */
- s->len += 4 + s->pklen; /* key blob */
- s->len += 4 + s->siglen; /* data to sign */
- s->len += 4; /* flags */
- s->agentreq = snewn(4 + s->len, char);
- PUT_32BIT(s->agentreq, s->len);
- s->q = s->agentreq + 4;
- *s->q++ = SSH2_AGENTC_SIGN_REQUEST;
- PUT_32BIT(s->q, s->pklen);
- s->q += 4;
- memcpy(s->q, s->pkblob, s->pklen);
- s->q += s->pklen;
- PUT_32BIT(s->q, s->siglen);
+ /* Ask agent for signature. */
+ s->siglen = s->pktout->length - 5 + 4 +
+ ssh->v2_session_id_len;
+ if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+ s->siglen -= 4;
+ s->len = 1; /* message type */
+ s->len += 4 + s->pklen; /* key blob */
+ s->len += 4 + s->siglen; /* data to sign */
+ s->len += 4; /* flags */
+ s->agentreq = snewn(4 + s->len, char);
+ PUT_32BIT(s->agentreq, s->len);
+ s->q = s->agentreq + 4;
+ *s->q++ = SSH2_AGENTC_SIGN_REQUEST;
+ PUT_32BIT(s->q, s->pklen);
+ s->q += 4;
+ memcpy(s->q, s->pkblob, s->pklen);
+ s->q += s->pklen;
+ PUT_32BIT(s->q, s->siglen);
+ s->q += 4;
+ /* Now the data to be signed... */
+ if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+ PUT_32BIT(s->q, ssh->v2_session_id_len);
s->q += 4;
- /* Now the data to be signed... */
- if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
- PUT_32BIT(s->q, ssh->v2_session_id_len);
- s->q += 4;
- }
- memcpy(s->q, ssh->v2_session_id,
- ssh->v2_session_id_len);
- s->q += ssh->v2_session_id_len;
- memcpy(s->q, s->pktout->data + 5,
- s->pktout->length - 5);
- s->q += s->pktout->length - 5;
- /* And finally the (zero) flags word. */
- PUT_32BIT(s->q, 0);
- if (!agent_query(s->agentreq, s->len + 4,
- &vret, &s->retlen,
- ssh_agent_callback, ssh)) {
- do {
- crReturnV;
- if (pktin) {
- bombout(("Unexpected data from server"
- " while waiting for agent"
- " response"));
- crStopV;
- }
- } while (pktin || inlen > 0);
- vret = ssh->agent_response;
- s->retlen = ssh->agent_response_len;
- }
- s->ret = vret;
- sfree(s->agentreq);
- if (s->ret) {
- if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
- logevent("Sending Pageant's response");
- ssh2_add_sigblob(ssh, s->pktout,
- s->pkblob, s->pklen,
- s->ret + 9,
- GET_32BIT(s->ret + 5));
- ssh2_pkt_send(ssh, s->pktout);
- s->authed = TRUE;
- break;
- } else {
- logevent
- ("Pageant failed to answer challenge");
- sfree(s->ret);
+ }
+ memcpy(s->q, ssh->v2_session_id,
+ ssh->v2_session_id_len);
+ s->q += ssh->v2_session_id_len;
+ memcpy(s->q, s->pktout->data + 5,
+ s->pktout->length - 5);
+ s->q += s->pktout->length - 5;
+ /* And finally the (zero) flags word. */
+ PUT_32BIT(s->q, 0);
+ if (!agent_query(s->agentreq, s->len + 4,
+ &vret, &s->retlen,
+ ssh_agent_callback, ssh)) {
+ do {
+ crReturnV;
+ if (pktin) {
+ bombout(("Unexpected data from server"
+ " while waiting for agent"
+ " response"));
+ crStopV;
}
+ } while (pktin || inlen > 0);
+ vret = ssh->agent_response;
+ s->retlen = ssh->agent_response_len;
+ }
+ s->ret = vret;
+ sfree(s->agentreq);
+ if (s->ret) {
+ if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
+ logevent("Sending Pageant's response");
+ ssh2_add_sigblob(ssh, s->pktout,
+ s->pkblob, s->pklen,
+ s->ret + 9,
+ GET_32BIT(s->ret + 5));
+ ssh2_pkt_send(ssh, s->pktout);
+ s->type = AUTH_TYPE_PUBLICKEY;
+ } else {
+ /* FIXME: less drastic response */
+ bombout(("Pageant failed to answer challenge"));
+ crStopV;
}
}
- if (s->authed)
- continue;
}
- sfree(s->response);
+
+ /* Do we have any keys left to try? */
+ if (s->pkblob_in_agent) {
+ s->done_agent = TRUE;
+ s->tried_pubkey_config = TRUE;
+ } else {
+ s->keyi++;
+ if (s->keyi >= s->nkeys)
+ s->done_agent = TRUE;
+ }
} else if (s->can_pubkey && s->publickey_blob &&
!s->tried_pubkey_config) {
/*
* Keyboard-interactive authentication.
*/
- char *name, *inst, *lang;
- int name_len, inst_len, lang_len;
- int i;
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
crWaitUntilV(pktin);
if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
/* Server is not willing to do keyboard-interactive
- * at all. Give up on it entirely. */
+ * at all (or, bizarrely but legally, accepts the
+ * user without actually issuing any prompts).
+ * Give up on it entirely. */
s->gotit = TRUE;
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE)
logevent("Keyboard-interactive authentication refused");
}
/*
- * We've got a fresh USERAUTH_INFO_REQUEST.
- * Get the preamble and start building a prompt.
+ * Loop while the server continues to send INFO_REQUESTs.
*/
- ssh_pkt_getstring(pktin, &name, &name_len);
- ssh_pkt_getstring(pktin, &inst, &inst_len);
- 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;
- }
- s->cur_prompt->instruction =
- dupprintf("Using keyboard-interactive authentication.%s%.*s",
- inst_len ? "\n" : "", inst_len, inst);
- s->cur_prompt->instr_reqd = TRUE;
+ while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
- /*
- * Get the prompts from the packet.
- */
- s->num_prompts = ssh_pkt_getuint32(pktin);
- for (i = 0; i < s->num_prompts; i++) {
- char *prompt;
- int prompt_len;
- int echo;
- static char noprompt[] =
- "<server failed to send prompt>: ";
+ char *name, *inst, *lang;
+ int name_len, inst_len, lang_len;
+ int i;
- ssh_pkt_getstring(pktin, &prompt, &prompt_len);
- echo = ssh2_pkt_getbool(pktin);
- if (!prompt_len) {
- prompt = noprompt;
- prompt_len = lenof(noprompt)-1;
+ /*
+ * We've got a fresh USERAUTH_INFO_REQUEST.
+ * Get the preamble and start building a prompt.
+ */
+ ssh_pkt_getstring(pktin, &name, &name_len);
+ ssh_pkt_getstring(pktin, &inst, &inst_len);
+ 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;
}
- add_prompt(s->cur_prompt,
- dupprintf("%.*s", prompt_len, prompt),
- echo, SSH_MAX_PASSWORD_LEN);
- }
+ /* 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 user's responses.
- */
- if (s->num_prompts) {
- int ret; /* not live over crReturn */
- ret = get_userpass_input(s->cur_prompt, NULL, 0);
- while (ret < 0) {
- ssh->send_ok = 1;
- crWaitUntilV(!pktin);
- ret = get_userpass_input(s->cur_prompt, in, inlen);
- ssh->send_ok = 0;
+ /*
+ * Get the prompts from the packet.
+ */
+ s->num_prompts = ssh_pkt_getuint32(pktin);
+ for (i = 0; i < s->num_prompts; i++) {
+ char *prompt;
+ int prompt_len;
+ int echo;
+ static char noprompt[] =
+ "<server failed to send prompt>: ";
+
+ ssh_pkt_getstring(pktin, &prompt, &prompt_len);
+ echo = ssh2_pkt_getbool(pktin);
+ if (!prompt_len) {
+ prompt = noprompt;
+ prompt_len = lenof(noprompt)-1;
+ }
+ add_prompt(s->cur_prompt,
+ dupprintf("%.*s", prompt_len, prompt),
+ echo, SSH_MAX_PASSWORD_LEN);
}
- if (!ret) {
- /*
- * Failed to get responses. Terminate.
- */
- free_prompts(s->cur_prompt);
- ssh_disconnect(ssh, NULL, "Unable to authenticate",
- SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
- TRUE);
- crStopV;
+
+ /*
+ * Get the user's responses.
+ */
+ if (s->num_prompts) {
+ int ret; /* not live over crReturn */
+ ret = get_userpass_input(s->cur_prompt, NULL, 0);
+ while (ret < 0) {
+ ssh->send_ok = 1;
+ crWaitUntilV(!pktin);
+ ret = get_userpass_input(s->cur_prompt, in, inlen);
+ ssh->send_ok = 0;
+ }
+ if (!ret) {
+ /*
+ * Failed to get responses. Terminate.
+ */
+ free_prompts(s->cur_prompt);
+ ssh_disconnect(ssh, NULL, "Unable to authenticate",
+ SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ TRUE);
+ crStopV;
+ }
}
+
+ /*
+ * Send the responses to the server.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ ssh2_pkt_adduint32(s->pktout, s->num_prompts);
+ for (i=0; i < s->num_prompts; i++) {
+ dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
+ ssh2_pkt_addstring(s->pktout,
+ s->cur_prompt->prompts[i]->result);
+ end_log_omission(ssh, s->pktout);
+ }
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
+
+ /*
+ * Get the next packet in case it's another
+ * INFO_REQUEST.
+ */
+ crWaitUntilV(pktin);
+
}
/*
- * Send the responses to the server.
+ * We should have SUCCESS or FAILURE now.
*/
- s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
- s->pktout->forcepad = 256;
- ssh2_pkt_adduint32(s->pktout, s->num_prompts);
- for (i=0; i < s->num_prompts; i++) {
- dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
- ssh2_pkt_addstring(s->pktout,
- s->cur_prompt->prompts[i]->result);
- end_log_omission(ssh, s->pktout);
- }
- ssh2_pkt_send(ssh, s->pktout);
- s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+ s->gotit = TRUE;
} else if (s->can_passwd) {
* people who find out how long their password is!
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- s->pktout->forcepad = 256;
ssh2_pkt_addstring(s->pktout, s->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
ssh2_pkt_addstring(s->pktout, s->password);
end_log_omission(ssh, s->pktout);
- ssh2_pkt_send(ssh, s->pktout);
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
logevent("Sent password");
s->type = AUTH_TYPE_PASSWORD;
s->cur_prompt->instruction =
dupprintf("%.*s", prompt_len, prompt);
s->cur_prompt->instr_reqd = TRUE;
+ /*
+ * There's no explicit requirement in the protocol
+ * for the "old" passwords in the original and
+ * password-change messages to be the same, and
+ * apparently some Cisco kit supports password change
+ * by the user entering a blank password originally
+ * and the real password subsequently, so,
+ * reluctantly, we prompt for the old password again.
+ *
+ * (On the other hand, some servers don't even bother
+ * to check this field.)
+ */
+ add_prompt(s->cur_prompt,
+ dupstr("Current password (blank for previously entered password): "),
+ FALSE, SSH_MAX_PASSWORD_LEN);
add_prompt(s->cur_prompt, dupstr("Enter new password: "),
FALSE, SSH_MAX_PASSWORD_LEN);
add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
}
/*
- * Check the two passwords match.
+ * If the user specified a new original password
+ * (IYSWIM), overwrite any previously specified
+ * one.
+ * (A side effect is that the user doesn't have to
+ * re-enter it if they louse up the new password.)
*/
- got_new = (strcmp(s->cur_prompt->prompts[0]->result,
- s->cur_prompt->prompts[1]->result)
+ if (s->cur_prompt->prompts[0]->result[0]) {
+ memset(s->password, 0, strlen(s->password));
+ /* burn the evidence */
+ sfree(s->password);
+ s->password =
+ dupstr(s->cur_prompt->prompts[0]->result);
+ }
+
+ /*
+ * Check the two new passwords match.
+ */
+ got_new = (strcmp(s->cur_prompt->prompts[1]->result,
+ s->cur_prompt->prompts[2]->result)
== 0);
if (!got_new)
/* They don't. Silly user. */
* (see above for padding rationale)
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- s->pktout->forcepad = 256;
ssh2_pkt_addstring(s->pktout, s->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
ssh2_pkt_addstring(s->pktout, s->password);
ssh2_pkt_addstring(s->pktout,
- s->cur_prompt->prompts[0]->result);
+ s->cur_prompt->prompts[1]->result);
free_prompts(s->cur_prompt);
end_log_omission(ssh, s->pktout);
- ssh2_pkt_send(ssh, s->pktout);
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
logevent("Sent new password");
/*
sfree(s->publickey_blob);
sfree(s->publickey_comment);
}
+ if (s->agent_response)
+ sfree(s->agent_response);
/*
* Now the connection protocol has started, one way or another.
/*
* Create the main session channel.
*/
- if (!ssh->cfg.ssh_no_shell) {
+ if (ssh->cfg.ssh_no_shell) {
+ ssh->mainchan = NULL;
+ } else if (*ssh->cfg.ssh_nc_host) {
+ /*
+ * Just start a direct-tcpip channel and use it as the main
+ * channel.
+ */
+ ssh->mainchan = snew(struct ssh_channel);
+ ssh->mainchan->ssh = ssh;
+ ssh->mainchan->localid = alloc_channel_id(ssh);
+ 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 = 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);
+ ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);
+ /*
+ * There's nothing meaningful to put in the originator
+ * fields, but some servers insist on syntactically correct
+ * information.
+ */
+ ssh2_pkt_addstring(s->pktout, "0.0.0.0");
+ ssh2_pkt_adduint32(s->pktout, 0);
+ ssh2_pkt_send(ssh, s->pktout);
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ bombout(("Server refused to open a direct-tcpip channel"));
+ crStopV;
+ /* FIXME: error data comes back in FAILURE packet */
+ }
+ if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
+ bombout(("Server's channel confirmation cited wrong channel"));
+ crStopV;
+ }
+ 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");
+ ssh->ncmode = TRUE;
+ } else {
ssh->mainchan = snew(struct ssh_channel);
ssh->mainchan->ssh = ssh;
ssh->mainchan->localid = alloc_channel_id(ssh);
add234(ssh->channels, ssh->mainchan);
update_specials_menu(ssh->frontend);
logevent("Opened channel for session");
- } else
- ssh->mainchan = NULL;
+ ssh->ncmode = FALSE;
+ }
/*
* Now we have a channel, make dispatch table entries for
/*
* Potentially enable X11 forwarding.
*/
- if (ssh->mainchan && ssh->cfg.x11_forward) {
+ if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
char proto[20], data[64];
logevent("Requesting X11 forwarding");
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
/*
* Potentially enable agent forwarding.
*/
- if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) {
+ if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {
logevent("Requesting OpenSSH-style agent forwarding");
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
/*
* Now allocate a pty for the session.
*/
- if (ssh->mainchan && !ssh->cfg.nopty) {
+ if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {
/* Unpick the terminal-speed string. */
/* XXX perhaps we should allow no speeds to be sent. */
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
* Simplest thing here is to send all the requests at once, and
* then wait for a whole bunch of successes or failures.
*/
- if (ssh->mainchan && *ssh->cfg.environmt) {
+ if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) {
char *e = ssh->cfg.environmt;
char *var, *varend, *val;
* this twice if the config data has provided a second choice
* of command.
*/
- if (ssh->mainchan) while (1) {
+ if (ssh->mainchan && !ssh->ncmode) while (1) {
int subsys;
char *cmd;
ssh1_throttle(ssh, -1);
}
} else {
- if (ssh->mainchan && ssh->mainchan->closes == 0)
- ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
+ ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
}
}
* too much hassle to keep track, and partly I'm not
* convinced the server should be told details like that
* about my local network configuration.
+ * The "originator IP address" is syntactically a numeric
+ * IP address, and some servers (e.g., Tectia) get upset
+ * if it doesn't match this syntax.
*/
- ssh2_pkt_addstring(pktout, "client-side-connection");
+ ssh2_pkt_addstring(pktout, "0.0.0.0");
ssh2_pkt_adduint32(pktout, 0);
ssh2_pkt_send(ssh, pktout);
}
}
-static Socket ssh_socket(void *handle)
+static int ssh_connected(void *handle)
{
Ssh ssh = (Ssh) handle;
- return ssh->s;
+ return ssh->s != NULL;
}
static int ssh_sendok(void *handle)
if (ssh->s != NULL)
return -1;
else
- return (ssh->exitcode >= 0 ? ssh->exitcode : 0);
+ return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX);
}
/*
ssh_size,
ssh_special,
ssh_get_specials,
- ssh_socket,
+ ssh_connected,
ssh_return_exitcode,
ssh_sendok,
ssh_ldisc,