(cp)[2] = (unsigned char)((value) >> 8); \
(cp)[3] = (unsigned char)(value); }
-enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM };
+/* Enumeration values for fields in SSH-1 packets */
+enum {
+ PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
+ /* These values are for communicating relevant semantics of
+ * fields to the packet logging code. */
+ PKTT_OTHER, PKTT_PASSWORD, PKTT_DATA
+};
/*
* Coroutine mechanics for the sillier bits of the code. If these
struct ssh_agent_channel {
unsigned char *message;
unsigned char msglen[4];
- int lensofar, totallen;
+ unsigned lensofar, totallen;
} a;
struct ssh_x11_channel {
Socket s;
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle);
static void ssh_do_close(Ssh ssh);
+static unsigned long ssh_pkt_getuint32(Ssh ssh);
+static int ssh2_pkt_getbool(Ssh ssh);
+static void ssh_pkt_getstring(Ssh ssh, char **p, int *length);
struct rdpkt1_state_tag {
long len, pad, biglen, to_read;
int deferred_len, deferred_size;
/*
+ * State associated with packet logging
+ */
+ int pktout_logmode;
+ int pktout_nblanks;
+ struct logblank_t *pktout_blanks;
+
+ /*
* Gross hack: pscp will try to start SFTP but fall back to
* scp1 if that fails. This variable is the means by which
* scp.c can reach into the SSH code and find out which one it
sfree(text); \
} while (0)
+/* Functions to leave bits out of the SSH packet log file. */
+
+static void dont_log_password(Ssh ssh, int blanktype)
+{
+ if (ssh->cfg.logomitpass)
+ ssh->pktout_logmode = blanktype;
+}
+
+static void dont_log_data(Ssh ssh, int blanktype)
+{
+ if (ssh->cfg.logomitdata)
+ ssh->pktout_logmode = blanktype;
+}
+
+static void end_log_omission(Ssh ssh)
+{
+ ssh->pktout_logmode = PKTLOG_EMIT;
+}
+
static int ssh_channelcmp(void *av, void *bv)
{
struct ssh_channel *a = (struct ssh_channel *) av;
ssh->pktin.type = ssh->pktin.body[-1];
- if (ssh->logctx)
+ /*
+ * Log incoming packet, possibly omitting sensitive fields.
+ */
+ if (ssh->logctx) {
+ int nblanks = 0;
+ struct logblank_t blank;
+ if (ssh->cfg.logomitdata) {
+ int do_blank = FALSE, blank_prefix = 0;
+ /* "Session data" packets - omit the data field */
+ if ((ssh->pktin.type == SSH1_SMSG_STDOUT_DATA) ||
+ (ssh->pktin.type == SSH1_SMSG_STDERR_DATA)) {
+ do_blank = TRUE; blank_prefix = 0;
+ } else if (ssh->pktin.type == SSH1_MSG_CHANNEL_DATA) {
+ do_blank = TRUE; blank_prefix = 4;
+ }
+ if (do_blank) {
+ blank.offset = blank_prefix;
+ blank.len = ssh->pktin.length;
+ blank.type = PKTLOG_OMIT;
+ nblanks = 1;
+ }
+ }
log_packet(ssh->logctx,
PKT_INCOMING, ssh->pktin.type,
ssh1_pkt_type(ssh->pktin.type),
- ssh->pktin.body, ssh->pktin.length);
+ ssh->pktin.body, ssh->pktin.length,
+ nblanks, &blank);
+ }
if (ssh->pktin.type == SSH1_SMSG_STDOUT_DATA ||
ssh->pktin.type == SSH1_SMSG_STDERR_DATA ||
}
if (ssh->pktin.type == SSH1_MSG_DEBUG) {
- /* log debug message */
- char buf[512];
- int stringlen = GET_32BIT(ssh->pktin.body);
- strcpy(buf, "Remote debug message: ");
- if (stringlen > 480)
- stringlen = 480;
- memcpy(buf + 8, ssh->pktin.body + 4, stringlen);
- buf[8 + stringlen] = '\0';
+ char *buf, *msg;
+ int msglen;
+
+ ssh_pkt_getstring(ssh, &msg, &msglen);
+ buf = dupprintf("Remote debug message: %.*s", msglen, msg);
logevent(buf);
+ sfree(buf);
+
goto next_packet;
} else if (ssh->pktin.type == SSH1_MSG_IGNORE) {
/* do nothing */
if (ssh->pktin.type == SSH1_MSG_DISCONNECT) {
/* log reason code in disconnect message */
- char buf[256];
- unsigned msglen = GET_32BIT(ssh->pktin.body);
- unsigned nowlen;
- strcpy(buf, "Remote sent disconnect: ");
- nowlen = strlen(buf);
- if (msglen > sizeof(buf) - nowlen - 1)
- msglen = sizeof(buf) - nowlen - 1;
- memcpy(buf + nowlen, ssh->pktin.body + 4, msglen);
- buf[nowlen + msglen] = '\0';
- /* logevent(buf); (this is now done within the bombout macro) */
- bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
+ char *msg;
+ int msglen;
+
+ ssh_pkt_getstring(ssh, &msg, &msglen);
+
+ bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));
crStop(0);
}
ssh->pktin.body = ssh->pktin.data;
ssh->pktin.type = ssh->pktin.data[5];
- if (ssh->logctx)
+ /*
+ * Log incoming packet, possibly omitting sensitive fields.
+ */
+ if (ssh->logctx) {
+ int nblanks = 0;
+ struct logblank_t blank;
+ if (ssh->cfg.logomitdata) {
+ int do_blank = FALSE, blank_prefix = 0;
+ /* "Session data" packets - omit the data field */
+ if (ssh->pktin.type == SSH2_MSG_CHANNEL_DATA) {
+ do_blank = TRUE; blank_prefix = 4;
+ } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
+ do_blank = TRUE; blank_prefix = 8;
+ }
+ if (do_blank) {
+ blank.offset = blank_prefix;
+ blank.len = (ssh->pktin.length-6) - blank_prefix;
+ blank.type = PKTLOG_OMIT;
+ nblanks = 1;
+ }
+ }
log_packet(ssh->logctx, PKT_INCOMING, ssh->pktin.type,
ssh2_pkt_type(ssh->pkt_ctx, ssh->pktin.type),
- ssh->pktin.data+6, ssh->pktin.length-6);
+ ssh->pktin.data+6, ssh->pktin.length-6,
+ nblanks, &blank);
+ }
switch (ssh->pktin.type) {
/*
case SSH2_MSG_DISCONNECT:
{
/* log reason code in disconnect message */
- char *buf;
- int nowlen;
- int reason = GET_32BIT(ssh->pktin.data + 6);
- unsigned msglen = GET_32BIT(ssh->pktin.data + 10);
+ char *buf, *msg;
+ int nowlen, reason, msglen;
+
+ reason = ssh_pkt_getuint32(ssh);
+ ssh_pkt_getstring(ssh, &msg, &msglen);
if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
buf = dupprintf("Received disconnect message (%s)",
logevent(buf);
sfree(buf);
buf = dupprintf("Disconnection message text: %n%.*s",
- &nowlen, msglen, ssh->pktin.data + 14);
+ &nowlen, msglen, msg);
logevent(buf);
bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
reason,
case SSH2_MSG_DEBUG:
{
/* log the debug message */
- char buf[512];
- /* int display = ssh->pktin.body[6]; */
- int stringlen = GET_32BIT(ssh->pktin.data+7);
- int prefix;
- strcpy(buf, "Remote debug message: ");
- prefix = strlen(buf);
- if (stringlen > (int)(sizeof(buf)-prefix-1))
- stringlen = sizeof(buf)-prefix-1;
- memcpy(buf + prefix, ssh->pktin.data + 11, stringlen);
- buf[prefix + stringlen] = '\0';
+ char *buf, *msg;
+ int msglen;
+ int always_display;
+
+ /* XXX maybe we should actually take notice of this */
+ always_display = ssh2_pkt_getbool(ssh);
+ ssh_pkt_getstring(ssh, &msg, &msglen);
+
+ buf = dupprintf("Remote debug message: %.*s", msglen, msg);
logevent(buf);
+ sfree(buf);
}
- goto next_packet; /* FIXME: print the debug message */
+ goto next_packet;
/*
* These packets we need do nothing about here.
{
ssh1_pktout_size(ssh, len);
ssh->pktout.type = type;
+ /* Initialise log omission state */
+ ssh->pktout_nblanks = 0;
+ ssh->pktout_blanks = NULL;
}
static int s_wrpkt_prepare(Ssh ssh)
if (ssh->logctx)
log_packet(ssh->logctx, PKT_OUTGOING, ssh->pktout.type,
ssh1_pkt_type(ssh->pktout.type),
- ssh->pktout.body, ssh->pktout.length);
+ ssh->pktout.body, ssh->pktout.length,
+ ssh->pktout_nblanks, ssh->pktout_blanks);
+ sfree(ssh->pktout_blanks); ssh->pktout_blanks = NULL;
+ ssh->pktout_nblanks = 0;
if (ssh->v1_compressing) {
unsigned char *compblk;
bn = va_arg(ap1, Bignum);
pktlen += ssh1_bignum_length(bn);
break;
+ case PKTT_PASSWORD:
+ case PKTT_DATA:
+ case PKTT_OTHER:
+ /* ignore this pass */
+ break;
default:
assert(0);
}
p = ssh->pktout.body;
while ((argtype = va_arg(ap2, int)) != PKT_END) {
+ int offset = p - ssh->pktout.body, len = 0;
switch (argtype) {
+ /* Actual fields in the packet */
case PKT_INT:
argint = va_arg(ap2, int);
PUT_32BIT(p, argint);
- p += 4;
+ len = 4;
break;
case PKT_CHAR:
argchar = (unsigned char) va_arg(ap2, int);
*p = argchar;
- p++;
+ len = 1;
break;
case PKT_DATA:
argp = va_arg(ap2, unsigned char *);
arglen = va_arg(ap2, int);
memcpy(p, argp, arglen);
- p += arglen;
+ len = arglen;
break;
case PKT_STR:
argp = va_arg(ap2, unsigned char *);
arglen = strlen((char *)argp);
PUT_32BIT(p, arglen);
memcpy(p + 4, argp, arglen);
- p += 4 + arglen;
+ len = arglen + 4;
break;
case PKT_BIGNUM:
bn = va_arg(ap2, Bignum);
- p += ssh1_write_bignum(p, bn);
+ len = ssh1_write_bignum(p, bn);
+ break;
+ /* Tokens for modifications to packet logging */
+ case PKTT_PASSWORD:
+ dont_log_password(ssh, PKTLOG_BLANK);
+ break;
+ case PKTT_DATA:
+ dont_log_data(ssh, PKTLOG_OMIT);
+ break;
+ case PKTT_OTHER:
+ end_log_omission(ssh);
break;
}
+ p += len;
+ /* Deal with logfile omission, if required. */
+ if (len && (ssh->pktout_logmode != PKTLOG_EMIT)) {
+ ssh->pktout_nblanks++;
+ ssh->pktout_blanks = sresize(ssh->pktout_blanks,
+ ssh->pktout_nblanks,
+ struct logblank_t);
+ ssh->pktout_blanks[ssh->pktout_nblanks-1].offset = offset;
+ ssh->pktout_blanks[ssh->pktout_nblanks-1].len = len;
+ ssh->pktout_blanks[ssh->pktout_nblanks-1].type =
+ ssh->pktout_logmode;
+ }
}
}
}
static void ssh2_pkt_adddata(Ssh ssh, void *data, int len)
{
+ if (ssh->pktout_logmode != PKTLOG_EMIT) {
+ ssh->pktout_nblanks++;
+ ssh->pktout_blanks = sresize(ssh->pktout_blanks, ssh->pktout_nblanks,
+ struct logblank_t);
+ ssh->pktout_blanks[ssh->pktout_nblanks-1].offset =
+ ssh->pktout.length - 6;
+ ssh->pktout_blanks[ssh->pktout_nblanks-1].len = len;
+ ssh->pktout_blanks[ssh->pktout_nblanks-1].type = ssh->pktout_logmode;
+ }
ssh->pktout.length += len;
ssh2_pkt_ensure(ssh, ssh->pktout.length);
memcpy(ssh->pktout.data + ssh->pktout.length - len, data, len);
static void ssh2_pkt_init(Ssh ssh, int pkt_type)
{
ssh->pktout.length = 5;
+ ssh->pktout_nblanks = 0; ssh->pktout_blanks = NULL;
ssh2_pkt_addbyte(ssh, (unsigned char) pkt_type);
}
static void ssh2_pkt_addbool(Ssh ssh, unsigned char value)
if (ssh->logctx)
log_packet(ssh->logctx, PKT_OUTGOING, ssh->pktout.data[5],
ssh2_pkt_type(ssh->pkt_ctx, ssh->pktout.data[5]),
- ssh->pktout.data + 6, ssh->pktout.length - 6);
+ ssh->pktout.data + 6, ssh->pktout.length - 6,
+ ssh->pktout_nblanks, ssh->pktout_blanks);
+ sfree(ssh->pktout_blanks); ssh->pktout_blanks = NULL;
+ ssh->pktout_nblanks = 0;
/*
* Compress packet payload.
(!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
!strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
!strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
- !strcmp(imp, "OSU_1.4alpha3")))) {
+ !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) {
/*
* These versions don't support SSH1_MSG_IGNORE, so we have
* to use a different defence against password length
} else {
send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
PKT_INT, c->remoteid,
+ PKTT_DATA,
PKT_INT, replylen,
PKT_DATA, sentreply, replylen,
+ PKTT_OTHER,
PKT_END);
}
if (reply)
randomstr = snewn(top + 1, char);
for (i = bottom; i <= top; i++) {
- if (i == pwlen)
+ if (i == pwlen) {
defer_packet(ssh, s->pwpkt_type,
- PKT_STR, s->password, PKT_END);
- else {
+ PKTT_PASSWORD, PKT_STR, s->password,
+ PKTT_OTHER, PKT_END);
+ } else {
for (j = 0; j < i; j++) {
do {
randomstr[j] = random_byte();
ss = s->password;
}
logevent("Sending length-padded password");
- send_packet(ssh, s->pwpkt_type, PKT_INT, len,
- PKT_DATA, ss, len, PKT_END);
+ send_packet(ssh, s->pwpkt_type, PKTT_PASSWORD,
+ PKT_INT, len, PKT_DATA, ss, len,
+ PKTT_OTHER, PKT_END);
} else {
/*
* The server has _both_
int len;
len = strlen(s->password);
logevent("Sending unpadded password");
- send_packet(ssh, s->pwpkt_type, PKT_INT, len,
- PKT_DATA, s->password, len, PKT_END);
+ send_packet(ssh, s->pwpkt_type,
+ PKTT_PASSWORD, PKT_INT, len,
+ PKT_DATA, s->password, len,
+ PKTT_OTHER, PKT_END);
}
} else {
- send_packet(ssh, s->pwpkt_type, PKT_STR, s->password, PKT_END);
+ send_packet(ssh, s->pwpkt_type, PKTT_PASSWORD,
+ PKT_STR, s->password, PKTT_OTHER, PKT_END);
}
}
logevent("Sent password");
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
PKT_INT, c->remoteid,
- PKT_INT, len, PKT_DATA, buf, len, PKT_END);
+ PKTT_DATA,
+ PKT_INT, len, PKT_DATA, buf, len,
+ PKTT_OTHER, PKT_END);
/*
* In SSH1 we can return 0 here - implying that forwarded
* connections are never individually throttled - because
}
}
if (sport && dport) {
+ /* Set up a description of the source port. */
+ static char *sportdesc;
+ sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s",
+ (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
+ (int)(*saddr?1:0), ":",
+ (int)(sserv ? strlen(sports) : 0), sports,
+ sserv, "(", sport, sserv, ")");
if (type == 'L') {
- pfd_addforward(host, dport, *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
- " forwarding to %s:%.*s%.*s%d%.*s",
- (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")",
- host,
- (int)(dserv ? strlen(dports) : 0), dports,
- dserv, "(", dport, dserv, ")");
+ /* Verbose description of the destination port */
+ char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s",
+ host,
+ (int)(dserv ? strlen(dports) : 0), dports,
+ dserv, "(", dport, dserv, ")");
+ const char *err = pfd_addforward(host, dport,
+ *saddr ? saddr : NULL,
+ sport, ssh, &ssh->cfg);
+ if (err) {
+ logeventf(ssh, "Local port %s forward to %s"
+ " failed: %s", sportdesc, dportdesc, err);
+ } else {
+ logeventf(ssh, "Local port %s forwarding to %s",
+ sportdesc, dportdesc);
+ }
+ sfree(dportdesc);
} else if (type == 'D') {
- pfd_addforward(NULL, -1, *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
- " doing SOCKS dynamic forwarding",
- (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")");
+ const char *err = pfd_addforward(NULL, -1,
+ *saddr ? saddr : NULL,
+ sport, ssh, &ssh->cfg);
+ if (err) {
+ logeventf(ssh, "Local port %s SOCKS dynamic forward"
+ " setup failed: %s", sportdesc, err);
+ } else {
+ logeventf(ssh, "Local port %s doing SOCKS"
+ " dynamic forwarding", sportdesc);
+ }
} else {
struct ssh_rportfwd *pf;
pf = snew(struct ssh_rportfwd);
logevent("Remote port forwarding enabled");
}
}
+ sfree(sportdesc);
}
}
}
} else {
while (inlen > 0) {
int len = min(inlen, 512);
- send_packet(ssh, SSH1_CMSG_STDIN_DATA,
- PKT_INT, len, PKT_DATA, in, len, PKT_END);
+ send_packet(ssh, SSH1_CMSG_STDIN_DATA, PKTT_DATA,
+ PKT_INT, len, PKT_DATA, in, len,
+ PKTT_OTHER, PKT_END);
in += len;
inlen -= len;
}
ssh->cscipher->text_name);
logeventf(ssh, "Initialised %.200s server->client encryption",
ssh->sccipher->text_name);
+ logeventf(ssh, "Initialised %.200s client->server MAC algorithm",
+ ssh->csmac->text_name);
+ logeventf(ssh, "Initialised %.200s server->client MAC algorithm",
+ ssh->scmac->text_name);
if (ssh->cscomp->text_name)
logeventf(ssh, "Initialised %s compression",
ssh->cscomp->text_name);
len = c->v.v2.remmaxpkt;
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_DATA);
ssh2_pkt_adduint32(ssh, c->remoteid);
+ dont_log_data(ssh, PKTLOG_OMIT);
ssh2_pkt_addstring_start(ssh);
ssh2_pkt_addstring_data(ssh, data, len);
+ end_log_omission(ssh);
ssh2_pkt_send(ssh);
bufchain_consume(&c->v.v2.outbuffer, len);
c->v.v2.remwindow -= len;
int siglen, retlen, len;
char *q, *agentreq, *ret;
int try_send;
+ int num_env, env_left, env_ok;
};
crState(do_ssh2_authconn_state);
ssh2_pkt_addstring_data(ssh, (char *)pub_blob,
pub_blob_len);
ssh2_pkt_send(ssh);
- logevent("Offered public key"); /* FIXME */
+ logevent("Offered public key");
crWaitUntilV(ispkt);
if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
ssh2_pkt_addstring(ssh, "ssh-connection"); /* service requested */
ssh2_pkt_addstring(ssh, "password");
ssh2_pkt_addbool(ssh, FALSE);
+ dont_log_password(ssh, PKTLOG_BLANK);
ssh2_pkt_addstring(ssh, s->password);
memset(s->password, 0, sizeof(s->password));
+ end_log_omission(ssh);
ssh2_pkt_defer(ssh);
/*
* We'll include a string that's an exact multiple of the
ssh2_pkt_adduint32(ssh, s->num_prompts);
}
if (s->need_pw) { /* only add pw if we just got one! */
+ dont_log_password(ssh, PKTLOG_BLANK);
ssh2_pkt_addstring(ssh, s->password);
memset(s->password, 0, sizeof(s->password));
+ end_log_omission(ssh);
s->curr_prompt++;
}
if (s->curr_prompt >= s->num_prompts) {
* point; there's no need to send SERVICE_REQUEST.
*/
+ ssh->channels = newtree234(ssh_channelcmp);
+
/*
- * So now create a channel with a session in it.
+ * Create the main session channel.
*/
- ssh->channels = newtree234(ssh_channelcmp);
- ssh->mainchan = snew(struct ssh_channel);
- ssh->mainchan->ssh = ssh;
- ssh->mainchan->localid = alloc_channel_id(ssh);
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN);
- ssh2_pkt_addstring(ssh, "session");
- ssh2_pkt_adduint32(ssh, ssh->mainchan->localid);
- ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
- ssh2_pkt_adduint32(ssh, ssh->mainchan->v.v2.locwindow);/* our window size */
- ssh2_pkt_adduint32(ssh, 0x4000UL); /* our max pkt size */
- ssh2_pkt_send(ssh);
- crWaitUntilV(ispkt);
- if (ssh->pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
- bombout(("Server refused to open a session"));
- crStopV;
- /* FIXME: error data comes back in FAILURE packet */
- }
- if (ssh_pkt_getuint32(ssh) != ssh->mainchan->localid) {
- bombout(("Server's channel confirmation cited wrong channel"));
- crStopV;
- }
- ssh->mainchan->remoteid = ssh_pkt_getuint32(ssh);
- ssh->mainchan->type = CHAN_MAINSESSION;
- ssh->mainchan->closes = 0;
- ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(ssh);
- ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(ssh);
- bufchain_init(&ssh->mainchan->v.v2.outbuffer);
- add234(ssh->channels, ssh->mainchan);
- logevent("Opened channel for session");
+ if (!ssh->cfg.ssh_no_shell) {
+ ssh->mainchan = snew(struct ssh_channel);
+ ssh->mainchan->ssh = ssh;
+ ssh->mainchan->localid = alloc_channel_id(ssh);
+ ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN);
+ ssh2_pkt_addstring(ssh, "session");
+ ssh2_pkt_adduint32(ssh, ssh->mainchan->localid);
+ ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
+ ssh2_pkt_adduint32(ssh, ssh->mainchan->v.v2.locwindow);/* our window size */
+ ssh2_pkt_adduint32(ssh, 0x4000UL); /* our max pkt size */
+ ssh2_pkt_send(ssh);
+ crWaitUntilV(ispkt);
+ if (ssh->pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ bombout(("Server refused to open a session"));
+ crStopV;
+ /* FIXME: error data comes back in FAILURE packet */
+ }
+ if (ssh_pkt_getuint32(ssh) != ssh->mainchan->localid) {
+ bombout(("Server's channel confirmation cited wrong channel"));
+ crStopV;
+ }
+ ssh->mainchan->remoteid = ssh_pkt_getuint32(ssh);
+ ssh->mainchan->type = CHAN_MAINSESSION;
+ ssh->mainchan->closes = 0;
+ ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(ssh);
+ ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(ssh);
+ bufchain_init(&ssh->mainchan->v.v2.outbuffer);
+ add234(ssh->channels, ssh->mainchan);
+ update_specials_menu(ssh->frontend);
+ logevent("Opened channel for session");
+ } else
+ ssh->mainchan = NULL;
/*
* Potentially enable X11 forwarding.
*/
- if (ssh->cfg.x11_forward) {
+ if (ssh->mainchan && ssh->cfg.x11_forward) {
char proto[20], data[64];
logevent("Requesting X11 forwarding");
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
}
}
if (sport && dport) {
+ /* Set up a description of the source port. */
+ static char *sportdesc;
+ sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s",
+ (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
+ (int)(*saddr?1:0), ":",
+ (int)(sserv ? strlen(sports) : 0), sports,
+ sserv, "(", sport, sserv, ")");
if (type == 'L') {
- pfd_addforward(host, dport, *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
- " forwarding to %s:%.*s%.*s%d%.*s",
- (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")",
- host,
- (int)(dserv ? strlen(dports) : 0), dports,
- dserv, "(", dport, dserv, ")");
+ /* Verbose description of the destination port */
+ char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s",
+ host,
+ (int)(dserv ? strlen(dports) : 0), dports,
+ dserv, "(", dport, dserv, ")");
+ const char *err = pfd_addforward(host, dport,
+ *saddr ? saddr : NULL,
+ sport, ssh, &ssh->cfg);
+ if (err) {
+ logeventf(ssh, "Local port %s forward to %s"
+ " failed: %s", sportdesc, dportdesc, err);
+ } else {
+ logeventf(ssh, "Local port %s forwarding to %s",
+ sportdesc, dportdesc);
+ }
+ sfree(dportdesc);
} else if (type == 'D') {
- pfd_addforward(NULL, -1, *saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
- logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s"
- " doing SOCKS dynamic forwarding",
- (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")");
+ const char *err = pfd_addforward(NULL, -1,
+ *saddr ? saddr : NULL,
+ sport, ssh, &ssh->cfg);
+ if (err) {
+ logeventf(ssh, "Local port %s SOCKS dynamic forward"
+ " setup failed: %s", sportdesc, err);
+ } else {
+ logeventf(ssh, "Local port %s doing SOCKS"
+ " dynamic forwarding", sportdesc);
+ }
} else {
struct ssh_rportfwd *pf;
pf = snew(struct ssh_rportfwd);
" to %s:%d", host, dport);
sfree(pf);
} else {
- logeventf(ssh, "Requesting remote port "
- "%.*s%.*s%.*s%.*s%d%.*s"
+ logeventf(ssh, "Requesting remote port %s"
" forward to %s:%.*s%.*s%d%.*s",
- (int)(*saddr?strlen(saddr):0),
- *saddr?saddr:NULL,
- (int)(*saddr?1:0), ":",
- (int)(sserv ? strlen(sports) : 0), sports,
- sserv, "(", sport, sserv, ")",
+ sportdesc,
host,
(int)(dserv ? strlen(dports) : 0), dports,
dserv, "(", dport, dserv, ")");
ssh2_pkt_init(ssh, SSH2_MSG_GLOBAL_REQUEST);
ssh2_pkt_addstring(ssh, "tcpip-forward");
ssh2_pkt_addbool(ssh, 1);/* want reply */
- if (*saddr)
+ if (*saddr) {
ssh2_pkt_addstring(ssh, saddr);
- if (ssh->cfg.rport_acceptall)
+ } else if (ssh->cfg.rport_acceptall) {
ssh2_pkt_addstring(ssh, "0.0.0.0");
- else
+ } else {
ssh2_pkt_addstring(ssh, "127.0.0.1");
+ }
ssh2_pkt_adduint32(ssh, sport);
ssh2_pkt_send(ssh);
}
}
}
+ sfree(sportdesc);
}
}
}
/*
* Potentially enable agent forwarding.
*/
- if (ssh->cfg.agentfwd && agent_exists()) {
+ if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) {
logevent("Requesting OpenSSH-style agent forwarding");
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
/*
* Now allocate a pty for the session.
*/
- if (!ssh->cfg.nopty) {
+ if (ssh->mainchan && !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 */
}
/*
+ * Send environment variables.
+ *
+ * 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) {
+ char *e = ssh->cfg.environmt;
+ char *var, *varend, *val;
+
+ s->num_env = 0;
+
+ while (*e) {
+ var = e;
+ while (*e && *e != '\t') e++;
+ varend = e;
+ if (*e == '\t') e++;
+ val = e;
+ while (*e) e++;
+ e++;
+
+ ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+ ssh2_pkt_addstring(ssh, "env");
+ ssh2_pkt_addbool(ssh, 1); /* want reply */
+ ssh2_pkt_addstring_start(ssh);
+ ssh2_pkt_addstring_data(ssh, var, varend-var);
+ ssh2_pkt_addstring(ssh, val);
+ ssh2_pkt_send(ssh);
+
+ s->num_env++;
+ }
+
+ logeventf(ssh, "Sent %d environment variables", s->num_env);
+
+ s->env_ok = 0;
+ s->env_left = s->num_env;
+
+ while (s->env_left > 0) {
+ do {
+ crWaitUntilV(ispkt);
+ if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ unsigned i = ssh_pkt_getuint32(ssh);
+ struct ssh_channel *c;
+ c = find234(ssh->channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ c->v.v2.remwindow += ssh_pkt_getuint32(ssh);
+ }
+ } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+
+ if (ssh->pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+ if (ssh->pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+ bombout(("Unexpected response to environment request:"
+ " packet type %d", ssh->pktin.type));
+ crStopV;
+ }
+ } else {
+ s->env_ok++;
+ }
+
+ s->env_left--;
+ }
+
+ if (s->env_ok == s->num_env) {
+ logevent("All environment variables successfully set");
+ } else if (s->env_ok == 0) {
+ logevent("All environment variables refused");
+ c_write_str(ssh, "Server refused to set environment variables\r\n");
+ } else {
+ logeventf(ssh, "%d environment variables refused",
+ s->num_env - s->env_ok);
+ c_write_str(ssh, "Server refused to set all environment variables\r\n");
+ }
+ }
+
+ /*
* Start a shell or a remote command. We may have to attempt
* this twice if the config data has provided a second choice
* of command.
*/
- while (1) {
+ if (ssh->mainchan) while (1) {
int subsys;
char *cmd;
*/
if (ssh->ldisc)
ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
- ssh->send_ok = 1;
+ if (ssh->mainchan)
+ ssh->send_ok = 1;
while (1) {
crReturnV;
s->try_send = FALSE;
/* Do pre-close processing on the channel. */
switch (c->type) {
case CHAN_MAINSESSION:
- break; /* nothing to see here, move along */
+ ssh->mainchan = NULL;
+ update_specials_menu(ssh->frontend);
+ break;
case CHAN_X11:
if (c->u.x11.s != NULL)
x11_close(c->u.x11.s);
/*
* See if that was the last channel left open.
+ * (This is only our termination condition if we're
+ * not running in -N mode.)
*/
- if (count234(ssh->channels) == 0) {
+ if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
logevent("All channels closed. Disconnecting");
#if 0
/*
ssh2_pkt_send(ssh);
}
} else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+ static const char *const reasons[] = {
+ "<unknown reason code>",
+ "Administratively prohibited",
+ "Connect failed",
+ "Unknown channel type",
+ "Resource shortage",
+ };
unsigned i = ssh_pkt_getuint32(ssh);
+ unsigned reason_code;
+ char *reason_string;
+ int reason_length;
+ char *message;
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
if (!c)
if (c->type != CHAN_SOCKDATA_DORMANT)
continue; /* dunno why they're failing this */
- logevent("Forwarded connection refused by server");
+ reason_code = ssh_pkt_getuint32(ssh);
+ if (reason_code >= lenof(reasons))
+ reason_code = 0; /* ensure reasons[reason_code] in range */
+ ssh_pkt_getstring(ssh, &reason_string, &reason_length);
+ message = dupprintf("Forwarded connection refused by"
+ " server: %s [%.*s]", reasons[reason_code],
+ reason_length, reason_string);
+ logevent(message);
+ sfree(message);
pfd_close(c->u.pfd.s);
unsigned localid;
char *type;
int typelen, want_reply;
+ int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
struct ssh_channel *c;
localid = ssh_pkt_getuint32(ssh);
* the request type string to see if it's something
* we recognise.
*/
- if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
- c == ssh->mainchan) {
- /* We recognise "exit-status" on the primary channel. */
- char buf[100];
- ssh->exitcode = ssh_pkt_getuint32(ssh);
- sprintf(buf, "Server sent command exit status %d",
- ssh->exitcode);
- logevent(buf);
- if (want_reply) {
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS);
- ssh2_pkt_adduint32(ssh, c->remoteid);
- ssh2_pkt_send(ssh);
+ if (c == ssh->mainchan) {
+ /*
+ * We recognise "exit-status" and "exit-signal" on
+ * the primary channel.
+ */
+ if (typelen == 11 &&
+ !memcmp(type, "exit-status", 11)) {
+
+ ssh->exitcode = ssh_pkt_getuint32(ssh);
+ logeventf(ssh, "Server sent command exit status %d",
+ ssh->exitcode);
+ reply = SSH2_MSG_CHANNEL_SUCCESS;
+
+ } else if (typelen == 11 &&
+ !memcmp(type, "exit-signal", 11)) {
+
+ int is_plausible = TRUE, is_int = FALSE;
+ char *fmt_sig = "", *fmt_msg = "";
+ char *msg;
+ int msglen = 0, core = FALSE;
+ /* ICK: older versions of OpenSSH (e.g. 3.4p1)
+ * provide an `int' for the signal, despite its
+ * having been a `string' in the drafts since at
+ * least 2001. (Fixed in session.c 1.147.) Try to
+ * infer which we can safely parse it as. */
+ {
+ unsigned char *p = ssh->pktin.body +
+ ssh->pktin.savedpos;
+ long len = ssh->pktin.length - ssh->pktin.savedpos;
+ unsigned long num = GET_32BIT(p); /* what is it? */
+ /* If it's 0, it hardly matters; assume string */
+ if (num == 0) {
+ is_int = FALSE;
+ } else {
+ int maybe_int = FALSE, maybe_str = FALSE;
+#define CHECK_HYPOTHESIS(offset, result) \
+ do { \
+ long q = offset; \
+ if (q >= 0 && q+4 <= len) { \
+ q = q + 4 + GET_32BIT(p+q); \
+ if (q >= 0 && q+4 <= len && \
+ (q = q + 4 + GET_32BIT(p+q)) && q == len) \
+ result = TRUE; \
+ } \
+ } while(0)
+ CHECK_HYPOTHESIS(4+1, maybe_int);
+ CHECK_HYPOTHESIS(4+num+1, maybe_str);
+#undef CHECK_HYPOTHESIS
+ if (maybe_int && !maybe_str)
+ is_int = TRUE;
+ else if (!maybe_int && maybe_str)
+ is_int = FALSE;
+ else
+ /* Crikey. Either or neither. Panic. */
+ is_plausible = FALSE;
+ }
+ }
+ if (is_plausible) {
+ if (is_int) {
+ /* Old non-standard OpenSSH. */
+ int signum = ssh_pkt_getuint32(ssh);
+ fmt_sig = dupprintf(" %d", signum);
+ } else {
+ /* As per the drafts. */
+ char *sig;
+ int siglen;
+ ssh_pkt_getstring(ssh, &sig, &siglen);
+ /* Signal name isn't supposed to be blank, but
+ * let's cope gracefully if it is. */
+ if (siglen) {
+ fmt_sig = dupprintf(" \"%.*s\"",
+ siglen, sig);
+ }
+ }
+ core = ssh2_pkt_getbool(ssh);
+ ssh_pkt_getstring(ssh, &msg, &msglen);
+ if (msglen) {
+ fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);
+ }
+ /* ignore lang tag */
+ } /* else don't attempt to parse */
+ logeventf(ssh, "Server exited on signal%s%s%s",
+ fmt_sig, core ? " (core dumped)" : "",
+ fmt_msg);
+ if (*fmt_sig) sfree(fmt_sig);
+ if (*fmt_msg) sfree(fmt_msg);
+ reply = SSH2_MSG_CHANNEL_SUCCESS;
+
}
} else {
/*
* or respond with CHANNEL_FAILURE, depending
* on want_reply.
*/
- if (want_reply) {
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE);
- ssh2_pkt_adduint32(ssh, c->remoteid);
- ssh2_pkt_send(ssh);
- }
+ reply = SSH2_MSG_CHANNEL_FAILURE;
+ }
+ if (want_reply) {
+ ssh2_pkt_init(ssh, reply);
+ ssh2_pkt_adduint32(ssh, c->remoteid);
+ ssh2_pkt_send(ssh);
}
} else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
char *type;
bombout(("Strange packet received: type %d", ssh->pktin.type));
crStopV;
}
- } else {
+ } else if (ssh->mainchan) {
/*
* We have spare data. Add it to the channel buffer.
*/
ssh->deferred_send_data = NULL;
ssh->deferred_len = 0;
ssh->deferred_size = 0;
+ ssh->pktout_logmode = PKTLOG_EMIT;
+ ssh->pktout_nblanks = 0;
+ ssh->pktout_blanks = NULL;
ssh->fallback_cmd = 0;
ssh->pkt_ctx = 0;
ssh->x11auth = NULL;
PKT_INT, ssh->term_height,
PKT_INT, ssh->term_width,
PKT_INT, 0, PKT_INT, 0, PKT_END);
- } else {
+ } else if (ssh->mainchan) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
ssh2_pkt_addstring(ssh, "window-change");
*/
static const struct telnet_special *ssh_get_specials(void *handle)
{
+ static const struct telnet_special ignore_special[] = {
+ {"IGNORE message", TS_NOP},
+ };
+ static const struct telnet_special ssh2_session_specials[] = {
+ {NULL, TS_SEP},
+ {"Break", TS_BRK},
+ /* These are the signal names defined by draft-ietf-secsh-connect-19.
+ * They include all the ISO C signals, but are a subset of the POSIX
+ * required signals. */
+ {"SIGINT (Interrupt)", TS_SIGINT},
+ {"SIGTERM (Terminate)", TS_SIGTERM},
+ {"SIGKILL (Kill)", TS_SIGKILL},
+ {"SIGQUIT (Quit)", TS_SIGQUIT},
+ {"SIGHUP (Hangup)", TS_SIGHUP},
+ {"More signals", TS_SUBMENU},
+ {"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM},
+ {"SIGFPE", TS_SIGFPE}, {"SIGILL", TS_SIGILL},
+ {"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV},
+ {"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2},
+ {NULL, TS_EXITMENU}
+ };
+ static const struct telnet_special specials_end[] = {
+ {NULL, TS_EXITMENU}
+ };
+ static struct telnet_special ssh_specials[lenof(ignore_special) +
+ lenof(ssh2_session_specials) +
+ lenof(specials_end)];
Ssh ssh = (Ssh) handle;
+ int i = 0;
+#define ADD_SPECIALS(name) \
+ do { \
+ assert((i + lenof(name)) <= lenof(ssh_specials)); \
+ memcpy(&ssh_specials[i], name, sizeof name); \
+ i += lenof(name); \
+ } while(0)
if (ssh->version == 1) {
- static const struct telnet_special ssh1_specials[] = {
- {"IGNORE message", TS_NOP},
- {NULL, 0}
- };
- return ssh1_specials;
+ /* Don't bother offering IGNORE if we've decided the remote
+ * won't cope with it, since we wouldn't bother sending it if
+ * asked anyway. */
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
+ ADD_SPECIALS(ignore_special);
} else if (ssh->version == 2) {
- static const struct telnet_special ssh2_specials[] = {
- {"Break", TS_BRK},
- {"IGNORE message", TS_NOP},
- {NULL, 0}
- };
- return ssh2_specials;
- } else
+ /* XXX add rekey, when implemented */
+ ADD_SPECIALS(ignore_special);
+ if (ssh->mainchan)
+ ADD_SPECIALS(ssh2_session_specials);
+ } /* else we're not ready yet */
+
+ if (i) {
+ ADD_SPECIALS(specials_end);
+ return ssh_specials;
+ } else {
return NULL;
+ }
+#undef ADD_SPECIALS
}
/*
}
if (ssh->version == 1) {
send_packet(ssh, SSH1_CMSG_EOF, PKT_END);
- } else {
+ } else if (ssh->mainchan) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_EOF);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
ssh2_pkt_send(ssh);
|| ssh->state == SSH_STATE_PREPACKET) return;
if (ssh->version == 1) {
logevent("Unable to send BREAK signal in SSH1");
- } else {
+ } else if (ssh->mainchan) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
ssh2_pkt_addstring(ssh, "break");
ssh2_pkt_send(ssh);
}
} else {
- /* do nothing */
+ /* Is is a POSIX signal? */
+ char *signame = NULL;
+ if (code == TS_SIGABRT) signame = "ABRT";
+ if (code == TS_SIGALRM) signame = "ALRM";
+ if (code == TS_SIGFPE) signame = "FPE";
+ if (code == TS_SIGHUP) signame = "HUP";
+ if (code == TS_SIGILL) signame = "ILL";
+ if (code == TS_SIGINT) signame = "INT";
+ if (code == TS_SIGKILL) signame = "KILL";
+ if (code == TS_SIGPIPE) signame = "PIPE";
+ if (code == TS_SIGQUIT) signame = "QUIT";
+ if (code == TS_SIGSEGV) signame = "SEGV";
+ if (code == TS_SIGTERM) signame = "TERM";
+ if (code == TS_SIGUSR1) signame = "USR1";
+ if (code == TS_SIGUSR2) signame = "USR2";
+ /* The SSH-2 protocol does in principle support arbitrary named
+ * signals, including signame@domain, but we don't support those. */
+ if (signame) {
+ /* It's a signal. */
+ if (ssh->version == 2 && ssh->mainchan) {
+ ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+ ssh2_pkt_addstring(ssh, "signal");
+ ssh2_pkt_addbool(ssh, 0);
+ ssh2_pkt_addstring(ssh, signame);
+ ssh2_pkt_send(ssh);
+ logeventf(ssh, "Sent signal SIG%s", signame);
+ }
+ } else {
+ /* Never heard of it. Do nothing */
+ }
}
}