/*
* Log an SSH packet.
+ * If n_blanks != 0, blank or omit some parts.
+ * Set of blanking areas must be in increasing order.
*/
void log_packet(void *handle, int direction, int type,
- char *texttype, void *data, int len)
+ char *texttype, void *data, int len,
+ int n_blanks, const struct logblank_t *blanks)
{
struct LogContext *ctx = (struct LogContext *)handle;
- int i, j;
char dumpdata[80], smalldata[5];
if (ctx->cfg.logtype != LGTYP_PACKETS)
if (!ctx->lgfp)
logfopen(ctx);
if (ctx->lgfp) {
+ int p = 0, b = 0, omitted = 0;
+ int output_pos = 0; /* NZ if pending output in dumpdata */
+
+ /* Packet header. */
fprintf(ctx->lgfp, "%s packet type %d / 0x%02x (%s)\r\n",
direction == PKT_INCOMING ? "Incoming" : "Outgoing",
type, type, texttype);
- for (i = 0; i < len; i += 16) {
- sprintf(dumpdata, " %08x%*s\r\n", i, 1+3*16+2+16, "");
- for (j = 0; j < 16 && i+j < len; j++) {
- int c = ((unsigned char *)data)[i+j];
- sprintf(smalldata, "%02x", c);
- dumpdata[10+2+3*j] = smalldata[0];
- dumpdata[10+2+3*j+1] = smalldata[1];
- dumpdata[10+1+3*16+2+j] = (isprint(c) ? c : '.');
+
+ /*
+ * Output a hex/ASCII dump of the packet body, blanking/omitting
+ * parts as specified.
+ */
+ while (p < len) {
+ int blktype;
+
+ /* Move to a current entry in the blanking array. */
+ while ((b < n_blanks) &&
+ (p >= blanks[b].offset + blanks[b].len))
+ b++;
+ /* Work out what type of blanking to apply to
+ * this byte. */
+ blktype = PKTLOG_EMIT; /* default */
+ if ((b < n_blanks) &&
+ (p >= blanks[b].offset) &&
+ (p < blanks[b].offset + blanks[b].len))
+ blktype = blanks[b].type;
+
+ /* If we're about to stop omitting, it's time to say how
+ * much we omitted. */
+ if ((blktype != PKTLOG_OMIT) && omitted) {
+ fprintf(ctx->lgfp, " (%d byte%s omitted)\r\n",
+ omitted, (omitted==1?"":"s"));
+ omitted = 0;
+ }
+
+ /* (Re-)initialise dumpdata as necessary
+ * (start of row, or if we've just stopped omitting) */
+ if (!output_pos && !omitted)
+ sprintf(dumpdata, " %08x%*s\r\n", p-(p%16), 1+3*16+2+16, "");
+
+ /* Deal with the current byte. */
+ if (blktype == PKTLOG_OMIT) {
+ omitted++;
+ } else {
+ int c;
+ if (blktype == PKTLOG_BLANK) {
+ c = 'X';
+ sprintf(smalldata, "XX");
+ } else { /* PKTLOG_EMIT */
+ c = ((unsigned char *)data)[p];
+ sprintf(smalldata, "%02x", c);
+ }
+ dumpdata[10+2+3*(p%16)] = smalldata[0];
+ dumpdata[10+2+3*(p%16)+1] = smalldata[1];
+ dumpdata[10+1+3*16+2+(p%16)] = (isprint(c) ? c : '.');
+ output_pos = (p%16) + 1;
+ }
+
+ p++;
+
+ /* Flush row if necessary */
+ if (((p % 16) == 0) || (p == len) || omitted) {
+ if (output_pos) {
+ strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n");
+ fputs(dumpdata, ctx->lgfp);
+ output_pos = 0;
+ }
}
- strcpy(dumpdata + 10+1+3*16+2+j, "\r\n");
- fputs(dumpdata, ctx->lgfp);
+
}
+
+ /* Tidy up */
+ if (omitted)
+ fprintf(ctx->lgfp, " (%d byte%s omitted)\r\n",
+ omitted, (omitted==1?"":"s"));
fflush(ctx->lgfp);
}
}
(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
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 ||
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) {
/*
{
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.
} 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
} 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;
}
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;
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) {
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;