Hi!
The following patch adds ability to configure the ssh client to
send SSH[2]_MSG_IGNORE packets of random length at random times
whithin a user-specified interval. The function is configured by
setting the config-file options BogusTrafficIntervalMax and
BogusTrafficIntervalMin, which defines the interval in seconds in
which the packets are randomly sent. It is disabled by default. It
suffices to set the Max value to enable the function. The Min then
defaults to 0. The packet size is not configurable, but hardcoded to
be random between 1-64 bytes.
The patch does not include any man-page update, but if this is
something that may go into the main tree, I could put something
together.
The gains of this functionality is not only to fool traffic
analysis; I'm sitting behind a firewall at work that does connection
tracking of TCP connections. The firewall has an idletimeout of
TCP connections of between 30-40 minutes. After the timeout expires
it just drops the packets related to that connection, and the ssh
session is dead. The random packets functionality remedies this
annoyance. Yes, sshd can do ClientAlive, but one isn't always in
control of the system one is connecting to. Firewall reconfiguration
is no easy option when dealing with a Windows NT-centric
IT-department...
Patch against openssh-2.9p2.
/Martin
diff -ur openssh-2.9p2/clientloop.c openssh-2.9p2.servalive/clientloop.c
--- openssh-2.9p2/clientloop.c Fri Apr 20 14:50:51 2001
+++ openssh-2.9p2.servalive/clientloop.c Wed Sep 26 12:58:38 2001
@@ -324,6 +324,9 @@
client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
int *maxfdp, int rekeying)
{
+ struct timeval tv, *tvp = &tv;
+ int ret;
+
/* Add any selections by the channel mechanism. */
channel_prepare_select(readsetp, writesetp, maxfdp, rekeying);
@@ -356,13 +359,29 @@
/*
* Wait for something to happen. This will suspend the process until
* some selected descriptor can be read, written, or has some other
- * event pending. Note: if you want to implement SSH_MSG_IGNORE
- * messages to fool traffic analysis, this might be the place to do
- * it: just have a random timeout for the select, and send a random
- * SSH_MSG_IGNORE packet when the timeout expires.
+ * event pending.
+ * Set a random timeout for the select, and send a random SSH_MSG_IGNORE
+ * packet when the timeout expires to fool traffic analysis.
*/
-
- if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+ if (options.bogus_traffic_interval_max) {
+ u_int32_t rand = arc4random();
+ u_int64_t timeusec;
+ static u_int64_t timebase = 0;
+ u_int32_t interval_min = options.bogus_traffic_interval_min;
+ u_int32_t interval_max = options.bogus_traffic_interval_max;
+
+ if (!timebase)
+ timebase = (interval_max - interval_min) * 1000000;
+ timeusec = timebase * rand / 0xffffffff;
+ timeusec += interval_min * 1000000;
+ tv.tv_sec = timeusec / 1000000;
+ tv.tv_usec = timeusec % 1000000;
+ debug3("Will send SSH_MSG_IGNORE in %lu.%lu s", tv.tv_sec,
tv.tv_usec);
+ }
+ else tvp = NULL;
+
+ ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
+ if (ret < 0) {
char buf[100];
/*
@@ -379,6 +398,17 @@
snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
buffer_append(&stderr_buffer, buf, strlen(buf));
quit_pending = 1;
+ }
+ else if (ret == 0) {
+ /*
+ * select() timed out. Send a SSH[2]_MSG_IGNORE packet of random
+ * size between 1 - 64 bytes.
+ */
+ u_int32_t rand = arc4random();
+ packet_send_ignore((rand & 0x3f) + 1);
+ packet_send();
+ packet_write_wait();
+ debug3("Sent SSH_MSG_IGNORE of size %d", (rand & 0x3f) + 1);
}
}
diff -ur openssh-2.9p2/readconf.c openssh-2.9p2.servalive/readconf.c
--- openssh-2.9p2/readconf.c Tue Apr 17 20:11:37 2001
+++ openssh-2.9p2.servalive/readconf.c Wed Sep 26 10:14:25 2001
@@ -111,7 +111,7 @@
oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
- oHostKeyAlgorithms
+ oHostKeyAlgorithms, oBogusTrafficIntervalMax, oBogusTrafficIntervalMin
} OpCodes;
/* Textual representations of the tokens. */
@@ -172,6 +172,8 @@
{ "compression", oCompression },
{ "compressionlevel", oCompressionLevel },
{ "keepalive", oKeepAlives },
+ { "BogusTrafficIntervalMax", oBogusTrafficIntervalMax },
+ { "BogusTrafficIntervalMin", oBogusTrafficIntervalMin },
{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
{ "loglevel", oLogLevel },
{ "dynamicforward", oDynamicForward },
@@ -394,6 +396,42 @@
intptr = &options->keepalives;
goto parse_flag;
+ case oBogusTrafficIntervalMax:
+ intptr = &options->bogus_traffic_interval_max;
+ arg = strdelim(&s);
+ if (!arg || *arg == '\0')
+ fatal("%.200s line %d: Missing argument.", filename, linenum);
+ if (arg[0] < '0' || arg[0] > '9')
+ fatal("%.200s line %d: Bad number.", filename, linenum);
+
+ /* Octal, decimal, or hex format? */
+ value = strtol(arg, &endofnumber, 0);
+ if (arg == endofnumber)
+ fatal("%.200s line %d: Bad number.", filename, linenum);
+ if (*activep && *intptr == -1)
+ *intptr = value;
+ if (options->bogus_traffic_interval_min >= value)
+ fatal("%.200s line %d: Bad value.", filename, linenum);
+ break;
+
+ case oBogusTrafficIntervalMin:
+ intptr = &options->bogus_traffic_interval_min;
+ arg = strdelim(&s);
+ if (!arg || *arg == '\0')
+ fatal("%.200s line %d: Missing argument.", filename, linenum);
+ if (arg[0] < '0' || arg[0] > '9')
+ fatal("%.200s line %d: Bad number.", filename, linenum);
+
+ /* Octal, decimal, or hex format? */
+ value = strtol(arg, &endofnumber, 0);
+ if (arg == endofnumber)
+ fatal("%.200s line %d: Bad number.", filename, linenum);
+ if (*activep && *intptr == -1)
+ *intptr = value;
+ if (options->bogus_traffic_interval_max <= value)
+ fatal("%.200s line %d: Bad value.", filename, linenum);
+ break;
+
case oNumberOfPasswordPrompts:
intptr = &options->number_of_password_prompts;
goto parse_int;
@@ -738,6 +776,8 @@
options->strict_host_key_checking = -1;
options->compression = -1;
options->keepalives = -1;
+ options->bogus_traffic_interval_max = -1;
+ options->bogus_traffic_interval_min = -1;
options->compression_level = -1;
options->port = -1;
options->connection_attempts = -1;
@@ -825,6 +865,10 @@
options->compression = 0;
if (options->keepalives == -1)
options->keepalives = 1;
+ if (options->bogus_traffic_interval_max == -1)
+ options->bogus_traffic_interval_max = 0;
+ if (options->bogus_traffic_interval_min == -1)
+ options->bogus_traffic_interval_min = 0;
if (options->compression_level == -1)
options->compression_level = 6;
if (options->port == -1)
diff -ur openssh-2.9p2/readconf.h openssh-2.9p2.servalive/readconf.h
--- openssh-2.9p2/readconf.h Tue Apr 17 20:11:37 2001
+++ openssh-2.9p2.servalive/readconf.h Wed Sep 26 13:36:16 2001
@@ -62,6 +62,14 @@
int compression_level; /* Compression level 1 (fast) to 9
* (best). */
int keepalives; /* Set SO_KEEPALIVE. */
+ int bogus_traffic_interval_max; /*
+ * max time value of SSH_MSG_IGNORE
+ * interval
+ */
+ int bogus_traffic_interval_min; /*
+ * min time value of SSH_MSG_IGNORE
+ * interval
+ */
LogLevel log_level; /* Level for logging. */
int port; /* Port to connect. */