The intent of this option is similar to "tls-auth" in openvpn[1]: To refuse to talk to anyone who doesn't know the shared secret. You could compare this to port knocking, in that it solves a similar problem. This also prevents RST attacks from killing an existing connection, even when attacker can sniff sequence numbers. This feature doesn't work through NAT, since the source and destination are signed. As IPv6 becomes more and more prevalent it'll become possible to use this in more and more deployments. The socket option is enabled *after* connection establishment, thus doesn't protect against SYN floods. This is because server doesn't know (in userspace) what the address of the peer is until they connect. Again because signed addresses. Setting up: * Add "TCPMD5 foorbarSecret" to sshd_config * SSH with "-oTCPMD5=foobarSecret" Patch attached, but it's an ugly patch for 7.1p. I didn't want to put too much effort into cleaning up and documenting everything if the concept itself would rejected outright. It needs formatting, portability, apply to CVS HEAD, and include file changes at least. But it works on Linux as is. Everything signed from fourth packet: 16:32:40.902764 IP 127.0.0.1.51216 > 127.0.0.1.2222: Flags [S], seq 2342692369, win 43690, options [mss 65495,sackOK,TS val 51101999 ecr 0,nop,wscale 7], length 0 16:32:40.902777 IP 127.0.0.1.2222 > 127.0.0.1.51216: Flags [S.], seq 514139093, ack 2342692370, win 43690, options [mss 65495,sackOK,TS val 51101999 ecr 51101999,nop,wscale 7], length 0 16:32:40.902789 IP 127.0.0.1.51216 > 127.0.0.1.2222: Flags [.], ack 1, win 342, options [nop,nop,TS val 51101999 ecr 51101999], length 0 16:32:40.903480 IP 127.0.0.1.51216 > 127.0.0.1.2222: Flags [P.], seq 1:22, ack 1, win 342, options [nop,nop,md5shared secret not supplied with -M, can't check - 2daa171c0c342b041da3cb79ecd1d11b,nop,nop,TS val 51101999 ecr 51101999], length 21 So, how about it? Worth cleaning up? [1] https://community.openvpn.net/openvpn/wiki/Hardening#Useof--tls-auth -- ☢ Thomas ☢
From: Thomas Habets <habets@xxxxxxxxxx> Date: Wed, 13 Jan 2016 16:31:09 +0000 Subject: [PATCH 1/1] TCP MD5SIG for OpenSSH The intent of this option is similar to "tls-auth" in openvpn[1]: To refuse to talk to anyone who doesn't know the shared secret. You could compare this to port knocking, in that it solves a similar problem. This also prevents RST attacks from killing an existing connection, even when attacker can sniff sequence numbers. The socket option is enabled *after* connection establishment, thus doesn't protect against SYN floods. This is because server doesn't know (in userspace) what the address of the peer is until they connect. This feature doesn't work through NAT, since the source and destination are signed. As IPv6 becomes more and more prevalent it'll become possible to use this in more and more deployments. Setting up: * Add "TCPMD5 foorbarSecret" to sshd_config * SSH with "-oTCPMD5=foobarSecret" Patch attached, but it's an ugly patch for 7.1p. I didn't want to put too much effort into cleaning up and documenting everything if the concept itself would rejected outright. It needs formatting, portability, and include file changes at least. But it works on Linux as is. [1] https://community.openvpn.net/openvpn/wiki/Hardening#Useof--tls-auth --- readconf.c | 9 ++++++++- readconf.h | 1 + servconf.c | 15 ++++++++++++++- servconf.h | 1 + sshconnect.c | 25 +++++++++++++++++++++++++ sshd.c | 26 ++++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 2 deletions(-) diff --git a/readconf.c b/readconf.c index 1d03bdf..5233b95 100644 --- a/readconf.c +++ b/readconf.c @@ -143,7 +143,7 @@ typedef enum { oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, - oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, + oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, oTCPMD5, oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, @@ -234,6 +234,7 @@ static struct { { "preferredauthentications", oPreferredAuthentications }, { "hostkeyalgorithms", oHostKeyAlgorithms }, { "bindaddress", oBindAddress }, + { "tcpmd5", oTCPMD5 }, #ifdef ENABLE_PKCS11 { "smartcarddevice", oPKCS11Provider }, { "pkcs11provider", oPKCS11Provider }, @@ -1038,6 +1039,10 @@ parse_char_array: charptr = &options->pkcs11_provider; goto parse_string; + case oTCPMD5: + charptr = &options->tcpmd5; + goto parse_string; + case oProxyCommand: charptr = &options->proxy_command; parse_command: @@ -1641,6 +1646,7 @@ initialize_options(Options * options) options->preferred_authentications = NULL; options->bind_address = NULL; options->pkcs11_provider = NULL; + options->tcpmd5 = NULL; options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->identities_only = - 1; @@ -2312,6 +2318,7 @@ dump_client_config(Options *o, const char *host) dump_cfg_string(oLogLevel, log_level_name(o->log_level)); dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC); dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); + dump_cfg_string(oTCPMD5, o->tcpmd5); dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); dump_cfg_string(oProxyCommand, o->proxy_command); dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); diff --git a/readconf.h b/readconf.h index bb2d552..41d2b64 100644 --- a/readconf.h +++ b/readconf.h @@ -88,6 +88,7 @@ typedef struct { char *preferred_authentications; char *bind_address; /* local socket address for connection to sshd */ char *pkcs11_provider; /* PKCS#11 provider */ + char *tcpmd5; int verify_host_key_dns; /* Verify host key using DNS */ int num_identity_files; /* Number of files for RSA/DSA identities. */ diff --git a/servconf.c b/servconf.c index 6c7a91e..6da77cb 100644 --- a/servconf.c +++ b/servconf.c @@ -149,6 +149,7 @@ initialize_server_options(ServerOptions *options) options->max_authtries = -1; options->max_sessions = -1; options->banner = NULL; + options->tcpmd5 = NULL; options->use_dns = -1; options->client_alive_interval = -1; options->client_alive_count_max = -1; @@ -423,7 +424,7 @@ typedef enum { sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, - sDeprecated, sUnsupported + sDeprecated, sTCPMD5, sUnsupported } ServerOpCodes; #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ @@ -538,6 +539,7 @@ static struct { { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_ALL }, + { "tcpmd5", sTCPMD5, SSHCFG_GLOBAL}, { "permittunnel", sPermitTunnel, SSHCFG_ALL }, { "permittty", sPermitTTY, SSHCFG_ALL }, { "permituserrc", sPermitUserRC, SSHCFG_ALL }, @@ -1555,6 +1557,17 @@ process_server_config_line(ServerOptions *options, char *line, charptr = &options->banner; goto parse_filename; + case sTCPMD5: + charptr = &options->tcpmd5; + + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + /* * These options can contain %X options expanded at * connect time, so that you can specify paths like: diff --git a/servconf.h b/servconf.h index f4137af..18295c7 100644 --- a/servconf.h +++ b/servconf.h @@ -155,6 +155,7 @@ typedef struct { int max_authtries; int max_sessions; char *banner; /* SSH-2 banner message */ + char *tcpmd5; int use_dns; int client_alive_interval; /* * poke the client this often to diff --git a/sshconnect.c b/sshconnect.c index 17fbe39..d13f005 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -42,6 +42,12 @@ #include <string.h> #include <unistd.h> +#include<fcntl.h> +#include<netinet/in.h> +#include<netinet/tcp.h> +#include<sys/types.h> +#include<sys/socket.h> + #include "xmalloc.h" #include "key.h" #include "hostfile.h" @@ -479,6 +485,25 @@ ssh_connect_direct(const char *host, struct addrinfo *aitop, break; /* Successful connection. */ } + if (sock != -1 && options.tcpmd5) { + struct tcp_md5sig md5sig; + socklen_t t = sizeof(struct sockaddr_storage); + + memset(&md5sig, 0, sizeof(md5sig)); + strlcpy(md5sig.tcpm_key, options.tcpmd5, TCP_MD5SIG_MAXKEYLEN); + + if (getpeername(sock, + (struct sockaddr*)&md5sig.tcpm_addr, &t)) { + error("getpeername(): %.100s", strerror(errno)); + } + md5sig.tcpm_keylen = strlen(md5sig.tcpm_key); + if (-1 == setsockopt(sock, + IPPROTO_TCP, TCP_MD5SIG, + &md5sig, sizeof(md5sig))) { + error("setsockopt(TCP_MD5SIG): %.100s", strerror(errno)); + } + } + /* Return failure if we didn't get a successful connection. */ if (sock == -1) { error("ssh: connect to host %s port %s: %s", diff --git a/sshd.c b/sshd.c index 65ef7e8..7128527 100644 --- a/sshd.c +++ b/sshd.c @@ -85,6 +85,13 @@ #include <prot.h> #endif +#include<fcntl.h> +#include<netinet/in.h> +#include<netinet/tcp.h> +#include<sys/types.h> +#include<sys/socket.h> + + #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" @@ -1310,6 +1317,25 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) usleep(100 * 1000); continue; } + if (options.tcpmd5) { + struct tcp_md5sig md5sig; + socklen_t t = sizeof(struct sockaddr_storage); + + memset(&md5sig, 0, sizeof(md5sig)); + strlcpy(md5sig.tcpm_key, options.tcpmd5, TCP_MD5SIG_MAXKEYLEN); + + if (getpeername(*newsock, + (struct sockaddr*)&md5sig.tcpm_addr, &t)) { + error("getpeername(): %.100s", strerror(errno)); + } + md5sig.tcpm_keylen = strlen(md5sig.tcpm_key); + if (-1 == setsockopt(*newsock, + IPPROTO_TCP, TCP_MD5SIG, + &md5sig, sizeof(md5sig))) { + error("setsockopt(TCP_MD5SIG): %.100s", strerror(errno)); + } + } + if (unset_nonblock(*newsock) == -1) { close(*newsock); continue; -- 2.6.0.rc2.230.g3dd15c0
_______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev