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);
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;
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);
/*
* 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);
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. */
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);
/*
* 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;