Frank Bergmann http://www.tuxad.com 2014-11-08 http://www.tuxad.com/patches/jabberd-2.2.17.cipher-suite-and-forward-secrecy.patch forward secrecy patch for jabberd-2.2.17, short description ----------------------------------------------------------- This patch modifies or creates the following files: - Makefile (tepmplates) - sx/ssl.c - sx/sx.h - sx/tls-dh.c and sx/tls-dh-callback.c The patch contains two enhancements: The default cipher list is set to a "modern" value which provides security and some light backward compatibility. The first ciphers in the list are all DH based ciphers for ephemeral keys with "simple" AES and RC4-SHA as last fallback. The cipher list is configurable by a file /etc/jabberd/SSLCipherSuite at launch time. If this file does not exist then the builti default will be used. The file must not contain anything other than one line in "OpenSSL style". A LF as line termination is allowed and will be stripped. To create DH keys a set of pre-computed DH params with 512, 1024 and 2048 are built in. Additionally the files /etc/jabberd/dh512.pem /etc/jabberd/dh1024.pem /etc/jabberd/dh2048.pem will be read if they exist. For new ssl connections the files will be checked again for existance. If the modtime of the needed file did change then it will be read. You should regularly create new params (i.e. by cron) with something like openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem For package maintainers: When building a package please substitute the pre-computed values in sx/tls-dh.c by calling openssl dhparam -C 512 -noout >sx/tls-dh.c openssl dhparam -C 1024 -noout >>sx/tls-dh.c openssl dhparam -C 2048 -noout >>sx/tls-dh.c on package build time. Please also do this for the packaged DH param files in /etc/jabberd. As of 2014-11-08 there's no official jabberd2 release with DH / forward secrecy support. On github there's a commit of 2014-03-17 in the jabberd2 repository for DH support. But this commit seems to use only pre-computed DH params without making them configurable. This does not provide *real* forward secrecy. Apple's ChatServer is based on jabberd-2.2.17, too. But this patch was not tested with ChatServer. The patch offers configurable DH params and cipher suite in a very raw way. It seems to work but use it at your own risk. :-) Frank Bergmann 2014-11-08 diff -Naur jabberd-2.2.17.orig/sx/Makefile.am jabberd-2.2.17/sx/Makefile.am --- jabberd-2.2.17.orig/sx/Makefile.am 2011-10-22 21:56:00.000000000 +0200 +++ jabberd-2.2.17/sx/Makefile.am 2014-11-08 13:09:02.000000000 +0100 @@ -1,3 +1,4 @@ +INCLUDES = -DCONFIG_DIR=\"$(sysconfdir)\" LIBTOOL += --quiet noinst_LTLIBRARIES = libsx.la diff -Naur jabberd-2.2.17.orig/sx/Makefile.in jabberd-2.2.17/sx/Makefile.in --- jabberd-2.2.17.orig/sx/Makefile.in 2012-08-26 13:59:55.000000000 +0200 +++ jabberd-2.2.17/sx/Makefile.in 2014-11-08 13:09:02.000000000 +0100 @@ -219,6 +219,7 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ +INCLUDES = -DCONFIG_DIR=\"$(sysconfdir)\" noinst_LTLIBRARIES = libsx.la noinst_HEADERS = plugins.h sasl.h sx.h libsx_la_SOURCES = callback.c chain.c client.c env.c error.c io.c \ diff -Naur jabberd-2.2.17.orig/sx/ssl.c jabberd-2.2.17/sx/ssl.c --- jabberd-2.2.17.orig/sx/ssl.c 2012-02-12 22:38:25.000000000 +0100 +++ jabberd-2.2.17/sx/ssl.c 2014-11-08 16:50:23.000000000 +0100 @@ -25,7 +25,11 @@ #include "sx.h" #include +#include "sx/tls-dh-callback.c" +char *ssl_cipher_suite = 0; +char cipher_suite_buf[1025]; +char default_cipher_suite[] = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ALL:+aRSA:!NULL:!ADH:!EXP:!3DES:!kKRB5:!aDSS:!DES:!aPSK:!kECDH:!SEED:!RC2:!IDEA:!CAMELLIA:!AES256-GCM-SHA384:!AES256-SHA256:!AES256-SHA:!AES128-GCM-SHA256:!AES128-SHA256:!kECDH:!AECDH:!MD5:AES128-SHA:RC4-SHA"; /* code stolen from SSL_CTX_set_verify(3) */ static int _sx_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) @@ -864,6 +868,7 @@ STACK_OF(X509_NAME) *cert_names; X509_STORE * store; int ret; + int fd; if(!sx_openssl_initialized) { _sx_debug(ZONE, "ssl plugin not initialised"); @@ -887,7 +892,24 @@ } // Set allowed ciphers - if (SSL_CTX_set_cipher_list(ctx, "ALL:!LOW:!SSLv2:!EXP:!aNULL") != 1) { + if (ssl_cipher_suite == 0) { + fd = open(CONFIG_DIR "/SSLCipherSuite", O_RDONLY); + if (fd >= 0) { + int len; + len = read(fd, cipher_suite_buf, 1024); + if (len > 1) { + cipher_suite_buf[len] = 0; + --len; + if (cipher_suite_buf[len] == '\n') cipher_suite_buf[len] = 0; + ssl_cipher_suite = cipher_suite_buf; + } + close(fd); + } + } + if (ssl_cipher_suite == 0) { + ssl_cipher_suite = default_cipher_suite; + } + if (SSL_CTX_set_cipher_list(ctx, ssl_cipher_suite) != 1) { _sx_debug(ZONE, "Can't set cipher list for SSL context: %s", ERR_error_string(ERR_get_error(), NULL)); SSL_CTX_free(ctx); return 1; @@ -947,6 +969,17 @@ return 1; } +/* + DH *dh = get_dh1024(); + ret = SSL_CTX_set_tmp_dh (ctx, dh); + if(ret != 1) { + _sx_debug(ZONE, "couldn't set dh params with SSL_CTX_set_tmp_dh()"); + DH_free (dh); + return 1; + } +*/ + SSL_CTX_set_tmp_dh_callback(ctx, sx_ssl_tmp_dh_callback); + _sx_debug(ZONE, "setting ssl context '%s' verify mode to %02x", name, mode); SSL_CTX_set_verify(ctx, mode, _sx_ssl_verify_callback); diff -Naur jabberd-2.2.17.orig/sx/sx.h jabberd-2.2.17/sx/sx.h --- jabberd-2.2.17.orig/sx/sx.h 2012-02-12 21:38:07.000000000 +0100 +++ jabberd-2.2.17/sx/sx.h 2014-11-08 13:09:02.000000000 +0100 @@ -29,6 +29,9 @@ #include #include +#include +#include +#include /* jabberd2 Windows DLL */ #ifndef JABBERD2_API diff -Naur jabberd-2.2.17.orig/sx/tls-dh.c jabberd-2.2.17/sx/tls-dh.c --- jabberd-2.2.17.orig/sx/tls-dh.c 1970-01-01 01:00:00.000000000 +0100 +++ jabberd-2.2.17/sx/tls-dh.c 2014-11-08 13:09:02.000000000 +0100 @@ -0,0 +1,93 @@ +#ifndef HEADER_DH_H +#include +#endif + +DH *get_dh512() + { + static unsigned char dh512_p[]={ + 0xFB,0x09,0x6D,0x12,0xFC,0xA2,0x42,0x9A,0x67,0x04,0xF1,0xB1, + 0x5D,0x5D,0xFB,0xA1,0xA7,0x85,0x4C,0x90,0x6D,0xFB,0xCF,0x01, + 0x83,0xEA,0xD0,0x50,0x7F,0x12,0x72,0x6B,0xF7,0xAE,0x39,0xE4, + 0x55,0x8D,0x47,0x55,0xCF,0x0F,0x8C,0x78,0xA3,0x49,0x6F,0xE0, + 0x17,0x62,0xED,0x5A,0x88,0x21,0x52,0xA9,0x21,0x19,0xB6,0x10, + 0x59,0xC3,0xCE,0x7B, + }; + static unsigned char dh512_g[]={ + 0x02, + }; + DH *dh; + + if ((dh=DH_new()) == NULL) return(NULL); + dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL); + dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + { DH_free(dh); return(NULL); } + return(dh); + } + +DH *get_dh1024() + { + static unsigned char dh1024_p[]={ + 0xB1,0xF5,0x95,0x80,0xFD,0x1C,0xC8,0x3C,0x29,0xF9,0x73,0xD2, + 0xAD,0x1E,0x54,0x88,0x54,0x58,0x85,0xFC,0xC9,0x3F,0xDA,0xD9, + 0x4C,0xA7,0x8E,0x0C,0x22,0xF7,0xD0,0x1D,0x78,0xC2,0x8F,0xF7, + 0x3F,0x02,0x80,0x8B,0x32,0x5D,0xF9,0x45,0x73,0x36,0x5B,0x0E, + 0xA6,0x23,0x6A,0x1C,0xD8,0x91,0xA9,0x0E,0x71,0xEE,0x61,0x9F, + 0x4E,0x38,0x94,0xA7,0x49,0x85,0x80,0xE5,0xC0,0x2D,0xB3,0x7A, + 0xF4,0x47,0xCD,0x10,0x1E,0xC9,0x99,0xEE,0x27,0x5C,0x3A,0x8B, + 0x47,0x88,0x44,0x3C,0x13,0x92,0x7F,0xEF,0xD8,0x29,0xB2,0x75, + 0x62,0x86,0xE2,0x88,0x7D,0x2E,0x79,0x26,0x16,0x08,0x74,0xD7, + 0xE1,0xB0,0x1B,0x4C,0xB5,0xF8,0x25,0x54,0xC9,0x98,0xF6,0xB2, + 0x35,0x65,0x9B,0x25,0xDB,0xA8,0xBA,0x2B, + }; + static unsigned char dh1024_g[]={ + 0x02, + }; + DH *dh; + + if ((dh=DH_new()) == NULL) return(NULL); + dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL); + dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + { DH_free(dh); return(NULL); } + return(dh); + } + +DH *get_dh2048() + { + static unsigned char dh2048_p[]={ + 0xB0,0x61,0xAB,0x20,0xEF,0x04,0x47,0xF5,0x5D,0x97,0xCE,0x30, + 0xFB,0x15,0x1A,0x6E,0x09,0x37,0x0B,0x6A,0x35,0x35,0x5F,0xF5, + 0xA2,0x97,0xC1,0x79,0xAB,0xD1,0x2D,0x07,0x45,0x5E,0x13,0xA0, + 0x47,0x9D,0xAB,0x9C,0x18,0x52,0x0F,0xE4,0x9E,0x07,0xEB,0x22, + 0x5E,0xE1,0xF8,0x35,0x79,0x22,0x1B,0x5E,0x65,0xED,0xB4,0x22, + 0xEE,0xB3,0x32,0xAB,0xBE,0x71,0xFE,0x8D,0x4F,0x8F,0x8E,0x52, + 0x34,0x7B,0xAC,0x04,0x19,0x0F,0xAC,0x08,0x98,0xEE,0x44,0xF1, + 0x29,0x85,0xE9,0x0D,0x92,0xED,0xAA,0x75,0x96,0x7F,0xD7,0x17, + 0x40,0xC4,0xD6,0xE7,0xDF,0x14,0x5A,0x88,0x02,0x75,0x97,0x95, + 0xEE,0x85,0x5B,0xFF,0x19,0xCE,0x6B,0xFA,0xE0,0xBF,0x96,0xFC, + 0x58,0x47,0xD4,0xD7,0xB6,0xA7,0x96,0xC9,0x77,0x81,0x03,0xDD, + 0x07,0xE7,0xFE,0x23,0xB9,0x6A,0x1C,0x79,0xBB,0x4C,0x48,0x0A, + 0xD6,0x65,0xC7,0xE2,0xDD,0x7D,0x6F,0x5D,0x49,0xF5,0x40,0xFC, + 0xEE,0x69,0xBE,0xB6,0x05,0xC5,0xF6,0x80,0x64,0x6F,0xAF,0xB9, + 0xE5,0x88,0xCE,0xFB,0xAF,0x6D,0xC4,0x71,0xD2,0x3B,0xCB,0xBA, + 0xF1,0x9B,0x67,0xDF,0xAA,0x89,0x65,0x89,0xEC,0x39,0xFC,0xD6, + 0x8E,0xA0,0xBD,0x34,0xBB,0xE1,0x9D,0x3E,0x58,0x8D,0xEE,0x8F, + 0x05,0xE7,0x15,0xDE,0x34,0x10,0x5B,0xA9,0x7B,0xEC,0x29,0x9F, + 0xED,0x48,0x07,0x6D,0x58,0x0D,0xE4,0xF2,0xEF,0x6F,0xC1,0x93, + 0x67,0x85,0x48,0x72,0xA2,0x11,0xF0,0x16,0xD0,0xAC,0xA0,0xBB, + 0xEA,0x0B,0x4A,0xB3,0x07,0xB2,0x97,0x1A,0x55,0x35,0x13,0x38, + 0xCF,0x61,0x13,0xEB, + }; + static unsigned char dh2048_g[]={ + 0x02, + }; + DH *dh; + + if ((dh=DH_new()) == NULL) return(NULL); + dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); + dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + { DH_free(dh); return(NULL); } + return(dh); + } diff -Naur jabberd-2.2.17.orig/sx/tls-dh-callback.c jabberd-2.2.17/sx/tls-dh-callback.c --- jabberd-2.2.17.orig/sx/tls-dh-callback.c 1970-01-01 01:00:00.000000000 +0100 +++ jabberd-2.2.17/sx/tls-dh-callback.c 2014-11-08 16:48:33.000000000 +0100 @@ -0,0 +1,96 @@ +#include "sx.h" +#include +#include "sx/tls-dh.c" +#include +#include +#include +#include + +DH *dh_512 = 0; +DH *dh_1024 = 0; +DH *dh_2048 = 0; +time_t ts_dh_512 = 0; +time_t ts_dh_1024 = 0; +time_t ts_dh_2048 = 0; + +void readget_dh512() { + struct stat sb; + FILE *file; + + dh_512 = get_dh512(); /* default */ + + if (stat(CONFIG_DIR "/dh512.pem", &sb) == 0) { + if (ts_dh_512 != sb.st_mtime) { + /* timestamp has changed */ + file = fopen(CONFIG_DIR "/dh512.pem", "r"); + if (file) { + ts_dh_512 = sb.st_mtime; + dh_512 = PEM_read_DHparams(file, NULL, NULL, NULL); + fclose(file); + } + } + } + + return; +} + +void readget_dh1024() { + struct stat sb; + FILE *file; + + dh_1024 = get_dh1024(); /* default */ + + if (stat(CONFIG_DIR "/dh1024.pem", &sb) == 0) { + if (ts_dh_1024 != sb.st_mtime) { + /* timestamp has changed */ + file = fopen(CONFIG_DIR "/dh1024.pem", "r"); + if (file) { + ts_dh_1024 = sb.st_mtime; + dh_1024 = PEM_read_DHparams(file, NULL, NULL, NULL); + fclose(file); + } + } + } + + return; +} + +void readget_dh2048() { + struct stat sb; + FILE *file; + + dh_2048 = get_dh2048(); /* default */ + + if (stat(CONFIG_DIR "/dh2048.pem", &sb) == 0) { + if (ts_dh_2048 != sb.st_mtime) { + /* timestamp has changed */ + file = fopen(CONFIG_DIR "/dh2048.pem", "r"); + if (file) { + ts_dh_2048 = sb.st_mtime; + dh_2048 = PEM_read_DHparams(file, NULL, NULL, NULL); + fclose(file); + } + } + } + + return; +} + +static DH *sx_ssl_tmp_dh_callback(SSL *ssl, int export, int keylength) { + if (keylength == 512) { + readget_dh512(); + return dh_512; + } else { + if (keylength == 1024) { + readget_dh1024(); + return dh_1024; + } else { + if (keylength == 2048) { + readget_dh2048(); + return dh_2048; + } + } + } + return 0; +} +