diff -Nurd openssh-3.8p1.orig/Makefile.in openssh-3.8p1/Makefile.in --- openssh-3.8p1.orig/Makefile.in Wed Feb 18 05:35:11 2004 +++ openssh-3.8p1/Makefile.in Sun Apr 11 17:17:47 2004 @@ -99,6 +99,7 @@ -e 's|/etc/ssh/ssh_config|$(sysconfdir)/ssh_config|g' \ -e 's|/etc/ssh/ssh_known_hosts|$(sysconfdir)/ssh_known_hosts|g' \ -e 's|/etc/ssh/sshd_config|$(sysconfdir)/sshd_config|g' \ + -e 's|/etc/ssh/ssh_connection_cookies|$(sysconfdir)/ssh_connection_cookies|g' \ -e 's|/usr/libexec|$(libexecdir)|g' \ -e 's|/etc/shosts.equiv|$(sysconfdir)/shosts.equiv|g' \ -e 's|/etc/ssh/ssh_host_key|$(sysconfdir)/ssh_host_key|g' \ diff -Nurd openssh-3.8p1.orig/README.connectioncookies openssh-3.8p1/README.connectioncookies --- openssh-3.8p1.orig/README.connectioncookies Thu Jan 1 02:00:00 1970 +++ openssh-3.8p1/README.connectioncookies Sun Apr 11 17:17:47 2004 @@ -0,0 +1,83 @@ +Background +========== + +When an ssh client connects to an ssh server, there are very many lines +of code on the server side that the client is able to exercise before +successfully authenticating. Any vulnerability in this code can lead to +the server being compromised, even if the attacker is not in a position +to packet sniff a valid session. + +Many people use firewall rules or tcp-wrappers to limit the exposure of +this code to some small range of source IP addresses, but others want to +be able to connect from any IP address. + +Description +=========== + +The "Connection Cookies" mechanism is an unofficial ssh protocol +extension designed to prevent attackers who are unable to packet sniff +legitimate sessions from exploiting future ssh server vulnerabilities. + +The connection cookie is a shared secret, which the client sends to the +server very early in the conversation. Unless a valid cookie is received +the server drops the connection. + +Since the connection cookie mechanism is simple, there is very little +server side code that can be exercised by an attacker without a valid +connection cookie. + +Since the connection cookie is transmitted unencrypted, this extension +offers no extra protection against attackers who are able to packet sniff +legitimate sessions. + +(very rough) Specification +========================== + +An ssh server configured to require a connection cookie must prefix the +28-byte string "RequireSSHConnectionCookie\r\n" to its ident string. + +An ssh client configured to send connection cookies must do so only if +it sees this extra line from the server. The client should then prepend +the 3-byte string "CC:" followed by the 32-byte connection cookie to its +own version string. + +All 32 bytes of the connection cookie must be non-whitespace printable +US-ASCII characters other than doublequote (0x22). This restriction is +intended to simplify the handling of connection cookie values in +configuration files. + +If a digest of a passphrase is used as a connection cookie then the +passphrase should be both very hard to guess and uncorrelated with any +other passphrase, password or other sensitive information. Ssh clients +that accept such a connection passphrase and perform the digest +internally must use the MD5 digest algorithm and transmit the cookie as a +32-byte lowercase hexadecimal string. + +Disadvantages +============= + +There are a few ways in which an ssh server that requires connection +cookies may be less secure than one that does not: + +There may be a vulnerability of some sort in the server side code that +checks the connection cookie. However, that code is very simple so +vulnerabilities are unlikely. The same applies to the client side code +that sends the connection cookie. + +If each user has a different connection cookie, then a packet sniffing +attacker will be able to match up connection cookies to determine whether +or not any given pair of sessions belong to the same user. This may make +traffic analysis easier. + +If each user is given a different connection cookie, then a packet +sniffing attacker will be able to identify the first connection attempt +that a new user makes. The attacker my choose to attempt to impersonate +the server in these cases, hoping that the new client doesn't yet have +the server's public host key. The ability to confine such attacks to the +first connection attempts of new users may significantly reduce the risk +of detection. + + +-- +Nick Cleaton +catnick@cleaton.net (remove "cat") diff -Nurd openssh-3.8p1.orig/pathnames.h openssh-3.8p1/pathnames.h --- openssh-3.8p1.orig/pathnames.h Fri Feb 6 07:38:16 2004 +++ openssh-3.8p1/pathnames.h Sun Apr 11 17:17:47 2004 @@ -31,14 +31,15 @@ #define _PATH_SSH_SYSTEM_HOSTFILE2 SSHDIR "/ssh_known_hosts2" /* - * Of these, ssh_host_key must be readable only by root, whereas ssh_config - * should be world-readable. + * Of these, ssh_host_key and ssh_connection_cookies must be readable + * only by root, whereas ssh_config should be world-readable. */ #define _PATH_SERVER_CONFIG_FILE SSHDIR "/sshd_config" #define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" #define _PATH_HOST_KEY_FILE SSHDIR "/ssh_host_key" #define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" +#define _PATH_CONNECTION_COOKIES SSHDIR "/ssh_connection_cookies" #define _PATH_DH_MODULI SSHDIR "/moduli" /* Backwards compatibility */ #define _PATH_DH_PRIMES SSHDIR "/primes" diff -Nurd openssh-3.8p1.orig/readconf.c openssh-3.8p1/readconf.c --- openssh-3.8p1.orig/readconf.c Wed Dec 17 07:33:11 2003 +++ openssh-3.8p1/readconf.c Sun Apr 11 17:35:35 2004 @@ -106,6 +106,7 @@ oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, + oConnectionCookie, oDeprecated, oUnsupported } OpCodes; @@ -192,6 +193,7 @@ { "addressfamily", oAddressFamily }, { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, + { "connectioncookie", oConnectionCookie }, { NULL, oBadOption } }; @@ -744,6 +746,10 @@ intptr = &options->server_alive_count_max; goto parse_int; + case oConnectionCookie: + charptr = &options->connection_cookie; + goto parse_string; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -873,6 +879,7 @@ options->verify_host_key_dns = -1; options->server_alive_interval = -1; options->server_alive_count_max = -1; + options->connection_cookie = NULL; } /* @@ -991,6 +998,8 @@ options->server_alive_interval = 0; if (options->server_alive_count_max == -1) options->server_alive_count_max = 3; + if (options->connection_cookie == NULL) + options->connection_cookie = "-"; /* options->proxy_command should not be set by default */ /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ diff -Nurd openssh-3.8p1.orig/readconf.h openssh-3.8p1/readconf.h --- openssh-3.8p1.orig/readconf.h Wed Dec 17 07:33:11 2003 +++ openssh-3.8p1/readconf.h Sun Apr 11 17:25:42 2004 @@ -102,6 +102,8 @@ int no_host_authentication_for_localhost; int server_alive_interval; int server_alive_count_max; + + char *connection_cookie; } Options; diff -Nurd openssh-3.8p1.orig/servconf.c openssh-3.8p1/servconf.c --- openssh-3.8p1.orig/servconf.c Fri Jan 23 13:03:10 2004 +++ openssh-3.8p1/servconf.c Sun Apr 11 17:17:47 2004 @@ -101,6 +101,8 @@ options->client_alive_count_max = -1; options->authorized_keys_file = NULL; options->authorized_keys_file2 = NULL; + options->need_connection_cookie = -1; + options->connection_cookie_file = NULL; /* Needs to be accessable in many places */ use_privsep = -1; @@ -227,6 +229,10 @@ } if (options->authorized_keys_file == NULL) options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; + if (options->need_connection_cookie == -1) + options->need_connection_cookie = 0; + if (options->connection_cookie_file == NULL) + options->connection_cookie_file = _PATH_CONNECTION_COOKIES; /* Turn privilege separation on by default */ if (use_privsep == -1) @@ -268,6 +274,7 @@ sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sGssAuthentication, sGssCleanupCreds, sUsePrivilegeSeparation, + sNeedConnectionCookie, sConnectionCookieFile, sDeprecated, sUnsupported } ServerOpCodes; @@ -366,6 +373,8 @@ { "authorizedkeysfile", sAuthorizedKeysFile }, { "authorizedkeysfile2", sAuthorizedKeysFile2 }, { "useprivilegeseparation", sUsePrivilegeSeparation}, + { "needconnectioncookie", sNeedConnectionCookie}, + { "connectioncookiefile", sConnectionCookieFile}, { NULL, sBadOption } }; @@ -891,6 +900,14 @@ case sClientAliveCountMax: intptr = &options->client_alive_count_max; goto parse_int; + + case sNeedConnectionCookie: + intptr = &options->need_connection_cookie; + goto parse_flag; + + case sConnectionCookieFile: + charptr = &options->connection_cookie_file; + goto parse_filename; case sDeprecated: logit("%s line %d: Deprecated option %s", diff -Nurd openssh-3.8p1.orig/servconf.h openssh-3.8p1/servconf.h --- openssh-3.8p1.orig/servconf.h Wed Dec 31 02:37:34 2003 +++ openssh-3.8p1/servconf.h Sun Apr 11 17:17:47 2004 @@ -125,6 +125,8 @@ char *authorized_keys_file; /* File containing public keys */ char *authorized_keys_file2; int use_pam; /* Enable auth via PAM */ + int need_connection_cookie; /* If true, require a connection cookie */ + char *connection_cookie_file; /* File containing connection cookies */ } ServerOptions; void initialize_server_options(ServerOptions *); diff -Nurd openssh-3.8p1.orig/ssh.h openssh-3.8p1/ssh.h --- openssh-3.8p1.orig/ssh.h Tue Dec 9 10:15:12 2003 +++ openssh-3.8p1/ssh.h Sun Apr 11 17:17:47 2004 @@ -106,4 +106,14 @@ /* Listen backlog for sshd, ssh-agent and forwarding sockets */ #define SSH_LISTEN_BACKLOG 128 +/* The number of bytes in a connection cookie */ +#define SSH_CC_COOKIE_SIZE 32 + +/* + * The ident string prefix that identifies an ssh server as requiring + * a connection cookie. + */ +#define SSH_CC_BANNER "RequireSSHConnectionCookie" +#define SSH_CC_BANNER_LEN (sizeof(SSH_CC_BANNER)-1) + #endif /* SSH_H */ diff -Nurd openssh-3.8p1.orig/ssh_config.5 openssh-3.8p1/ssh_config.5 --- openssh-3.8p1.orig/ssh_config.5 Wed Dec 17 07:33:11 2003 +++ openssh-3.8p1/ssh_config.5 Sun Apr 11 17:17:47 2004 @@ -227,6 +227,16 @@ The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. +.It Cm ConnectionCookie +Specifies the value of the connection cookie to send to the server, if +it asks for one using the unofficial connection cookies protocol extension. +The value can be +.Dq - +to disable the sending of connection cookies, otherwise it must be a 32 +byte string of non-whitespace printable US-ASCII characters other than +.Dq \&" . +The default is +.Dq - . .It Cm ConnectTimeout Specifies the timeout (in seconds) used when connecting to the ssh server, instead of using the default system TCP timeout. diff -Nurd openssh-3.8p1.orig/sshconnect.c openssh-3.8p1/sshconnect.c --- openssh-3.8p1.orig/sshconnect.c Tue Jan 27 12:21:27 2004 +++ openssh-3.8p1/sshconnect.c Sun Apr 11 17:17:47 2004 @@ -440,6 +440,7 @@ int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); int minor1 = PROTOCOL_MINOR_1; + int need_connection_cookie = 0; /* Read other side\'s version identification. */ for (;;) { @@ -462,10 +463,21 @@ buf[sizeof(buf) - 1] = 0; if (strncmp(buf, "SSH-", 4) == 0) break; + if (strncmp(buf, SSH_CC_BANNER "\n", SSH_CC_BANNER_LEN+1) == 0) + need_connection_cookie = 1; debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); + /* Need a connection cookie if the server has asked for one. */ + if (need_connection_cookie) { + if (strcmp(options.connection_cookie, "-") == 0) + fatal("Server requires a connection cookie"); + if (strlen(options.connection_cookie) != SSH_CC_COOKIE_SIZE) + fatal("The configured connection cookie is not %d bytes long", + SSH_CC_COOKIE_SIZE); + } + /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. @@ -522,9 +534,12 @@ compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, compat20 ? PROTOCOL_MINOR_2 : minor1, SSH_VERSION); + client_version_string = xstrdup(buf); + if (need_connection_cookie) + snprintf(buf, sizeof buf, "CC:%.100s%.100s", options.connection_cookie, + client_version_string); if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) fatal("write: %.100s", strerror(errno)); - client_version_string = xstrdup(buf); chop(client_version_string); chop(server_version_string); debug("Local version string %.100s", client_version_string); diff -Nurd openssh-3.8p1.orig/sshd.c openssh-3.8p1/sshd.c --- openssh-3.8p1.orig/sshd.c Tue Feb 24 00:20:29 2004 +++ openssh-3.8p1/sshd.c Sun Apr 11 17:17:47 2004 @@ -377,12 +377,86 @@ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); server_version_string = xstrdup(buf); + /* Identify ourselves as requiring a connection cookie */ + if (options.need_connection_cookie) + snprintf(buf, sizeof buf, "%.50s\r\n%.150s", SSH_CC_BANNER, + server_version_string); + /* Send our protocol version identification. */ - if (atomicio(vwrite, sock_out, server_version_string, - strlen(server_version_string)) - != strlen(server_version_string)) { + if (atomicio(vwrite, sock_out, buf, strlen(buf)) != strlen(buf)) { logit("Could not write ident string to %s", get_remote_ipaddr()); cleanup_exit(255); + } + + /* Require a connection cookie, if configured. */ + if (options.need_connection_cookie) { + FILE *f; + int match; + char line[SSH_CC_COOKIE_SIZE + 100]; + char cookie[SSH_CC_COOKIE_SIZE]; + + memset(buf, 0, sizeof(buf)); + if (atomicio(read, sock_in, buf, 3) != 3) { + logit("Did not receive 'CC:' string from %s", + get_remote_ipaddr()); + cleanup_exit(255); + } + if (strncmp(buf, "CC:", 3)) { + logit("Client %s does not support connection cookies", + get_remote_ipaddr()); + cleanup_exit(255); + } + + for (i = 0; i < sizeof(cookie); i++) { + if (atomicio(read, sock_in, &cookie[i], 1) != 1) { + logit("Did not receive connection cookie from %s", + get_remote_ipaddr()); + cleanup_exit(255); + } + if ( cookie[i] == '\n' || cookie[i] == '\r' + || cookie[i] == '\0' || cookie[i] == '"' ) { + logit("Unexpected character in connection cookie from %s", + get_remote_ipaddr()); + cleanup_exit(255); + } + } + + f = fopen(options.connection_cookie_file, "r"); + if (!f) { + logit("Unable to open connection cookie file %s", + options.connection_cookie_file); + cleanup_exit(255); + } + + match = 0; + memset(line, 0, sizeof(line)); + while (fgets(line, sizeof(line), f)) { + int j, match_so_far; + + if (strlen(line) < SSH_CC_COOKIE_SIZE+2) + continue; + + match_so_far = 1; + + if (line[0] != '"') + match_so_far = 0; + for (j = 0; j < SSH_CC_COOKIE_SIZE; j++) + if (line[j+1] != cookie[j]) + match_so_far = 0; + if (line[SSH_CC_COOKIE_SIZE+1] != '"') + match_so_far = 0; + + if (match_so_far) + match = 1; + } + + fclose(f); + + if (!match) { + logit("Invalid connection cookie from %s", + get_remote_ipaddr()); + cleanup_exit(255); + } } /* Read other sides version identification. */ diff -Nurd openssh-3.8p1.orig/sshd_config.5 openssh-3.8p1/sshd_config.5 --- openssh-3.8p1.orig/sshd_config.5 Wed Feb 18 05:31:24 2004 +++ openssh-3.8p1/sshd_config.5 Sun Apr 11 17:17:47 2004 @@ -173,6 +173,27 @@ .Cm ClientAliveCountMax is left at the default, unresponsive ssh clients will be disconnected after approximately 45 seconds. +.It Cm ConnectionCookieFile +Specifies the file from which the set of valid connection cookies will be +read every time a new connection is made, if +.Cm NeedConnectionCookie +is set to +.Dq yes . +The file format is as follows: +.Pp +Lines starting with +.Ql \&" +followed by exactly 32 characters other than +.Ql \&" +followed by another +.Ql \&" +define connection cookies. All other lines are ignored. +.Pp +Connection cookies must consist of non-whitespace printable US-ASCII +characters only. +.Pp +The default is +.Dq /etc/ssh/ssh_connection_cookies . .It Cm Compression Specifies whether compression is allowed. The argument must be @@ -406,6 +427,27 @@ are refused if the number of unauthenticated connections reaches .Dq full (60). +.It Cm NeedConnectionCookie +Specifies whether clients must send connection cookies. If this option +is set to +.Dq yes +then only clients supporting the unofficial connection cookies protocol +extension and configured with a valid connection cookie will be able to +connect. The set of valid connection cookies is read from a file on the +server at each new connection, see +.Cm ConnectionCookieFile . +.Pp +With connection cookies enabled, attackers who are unable to packet sniff +a legitimate session or otherwise obtain a valid connection cookie are +unlikely to be able to exploit any future security vulnerabilities in +openssh. +.Pp +Enabling connection cookies may make some types of traffic analysis attack +easier to perform, particularly if each user has a different connection +cookie. +.Pp +The default is +.Dq no . .It Cm PasswordAuthentication Specifies whether password authentication is allowed. The default is