enable ephemeral Diffie-Hellman (aka EDH or DHE)

Daniel Kahn Gillmor <[email protected]> Fri, 25 Oct 2013 17:21:14 -0400
Newsgroups gmane.mail.perdition.user
Message-ID <[email protected]>
Hi Perdition folks--

I just noticed that when i operate perdition as a server offering TLS,
clients are unable to select an ephemeral Diffie-Hellman key exchange
mechanism (also known as EDH or DHE).  Since DHE is the most
widely-supported TLS key exchange mechanism to provide Perfect Forward
Secrecy (PFS), it seems like something perdition might want.

The patch below enables DHE support for perdition.  By default, it looks
for a PEM-encoded DH PARAMETERS section in the server's certificate
file.  I've also added a configuration option (--ssl_dh_params_file)
which can be used to specify a separate file for the DH params if
desired.

With the patch and --ssl_dh_params_file explicitly declared, perdition
will throw an error if no DH parameters could be loaded.  if
--ssl_dh_params_file isn't declared, it just tries to load DH params
From the cert file and carries on without DHE if no params can be found.

Another alternative could be to embed a default set of DH parameters
into perdition itself, if no parameters can be loaded.  I didn't
implement that, but could do so if it is desired.  Please let me know.

Also attached is a simple test script (reliant on gnutls-bin for setup)
that can be run from a built perdition source tree; if the built version
of perdition supports DHE, the script will leave the user in an IMAP
session with a test server (no backend attached, basically only LOGOUT
works).  If the built version of perdition doesn't work, then the script
will terminate.  Either way, copious diagnostic output is produced.

I'd be happy to have this feature adopted by perdition upstream, since i
have users of perdition who actively want to configure their MUAs to use
some PFS-enabled ciphersuite.

The patch is made against changeset 913:384a78e5951a.

Please let me know if there are changes you'd like to see, or if there
is anything that i should update to make the patch more acceptable for
inclusion upstream.

Thanks for perdition,

       --dkg

______________________________________________
Perdition-users mailing list
[email protected]
http://lists.vergenet.net/listinfo/perdition-users
dhe-test (text/sh, 1.1 KB)
#!/bin/sh

# Author: Daniel Kahn Gillmor <[email protected]>
# Date: 2013-10-24 08:52:54-0400

# set up a phony imap4s server on the specified port (defaults to
# 1233) with no backend configuration

port=${1:-1233}
priority=${2:-"NORMAL:-RSA"}

workdir=$(mktemp -d)

certtool --generate-privkey > "$workdir/server.key"
cat > "$workdir/certtool.template" <<EOF
cn = "localhost"
dns_name = "localhost"
serial = 1
expiration_days = 8
tls_www_server
signing_key
EOF
certtool --generate-self-signed \
    --load-privkey "$workdir/server.key" \
    --template "$workdir/certtool.template" \
    > "$workdir/server.pem"
certtool --generate-dh-params >> "$workdir/server.pem"

./perdition/perdition \
    --debug \
    --protocol IMAP4S \
    --ssl_cert_file "$workdir/server.pem" \
    --ssl_key_file "$workdir/server.key" \
    --listen_port "$port" \
    --map_library ./perdition/db/gdbm/.libs/libperditiondb_gdbm.so.0 \
    --pid_file "$(pwd)/../test.pid"

gnutls-cli-debug --port "$port" localhost

gnutls-cli --priority "$priority" --insecure --port "$port" localhost

kill "$(cat ../test.pid)"
rm -rf "$workdir"

tail /var/log/mail.log
add-dh-params-support-v4.patch (text/x-diff, 9.3 KB)
diff -r 384a78e5951a etc/perdition/perdition.conf
--- a/etc/perdition/perdition.conf	Fri Oct 04 11:44:06 2013 +0900
+++ b/etc/perdition/perdition.conf	Fri Oct 25 17:07:36 2013 -0400
@@ -358,6 +358,11 @@
 # (default "/etc/perdition/perdition.crt.pem")
 #ssl_cert_file /etc/perdition/perdition.crt.pem
 
+# ssl_dh_params_file FILENAME:
+# Diffie-Hellman parameters to use when offering EDH ciphersuites to clients.
+# (default is to look for the DH params in the ssl_cert_file)
+#ssl_dh_params_file /etc/perdition/perdition.crt.pem
+
 # ssl_cert_accept_self_signed:
 # Accept self-signed certificates.
 #ssl_cert_accept_self_signed
diff -r 384a78e5951a perdition/options.c
--- a/perdition/options.c	Fri Oct 04 11:44:06 2013 +0900
+++ b/perdition/options.c	Fri Oct 25 17:07:36 2013 -0400
@@ -464,6 +464,8 @@
       TAG_SSL_CA_ACCEPT_SELF_SIGNED, NULL, NULL},
     {"ssl_cert_file",               '\0', POPT_ARG_STRING, NULL,
       TAG_SSL_CERT_FILE, NULL, NULL},
+    {"ssl_dh_params_file",          '\0', POPT_ARG_STRING, NULL,
+      TAG_SSL_DH_PARAMS_FILE, NULL, NULL},
     {"ssl_cert_accept_expired",     '\0', POPT_ARG_NONE,   NULL,
       TAG_SSL_CERT_ACCEPT_EXPIRED, NULL, NULL},
     {"ssl_cert_accept_not_yet_valid", '\0', POPT_ARG_NONE, NULL,
@@ -594,6 +596,7 @@
     opt_i(&(opt.ssl_ca_accept_self_signed), DEFAULT_SSL_CA_ACCEPT_SELF_SIGNED, 
 		    &i, 0, OPT_NOT_SET);
     opt_p(&(opt.ssl_cert_file), DEFAULT_SSL_CERT_FILE, &i, 0, OPT_NOT_SET);
+    opt_p(&(opt.ssl_dh_params_file), DEFAULT_SSL_DH_PARAMS_FILE, &i, 0, OPT_NOT_SET);
     opt_i(&(opt.ssl_cert_accept_expired), DEFAULT_SSL_CERT_ACCEPT_EXPIRED, 
 	       	    &i, 0, OPT_NOT_SET);
     opt_i(&(opt.ssl_cert_accept_not_yet_valid), 
@@ -920,6 +923,14 @@
 	NO_SSL_OPT("ssl_cert_file");
 #endif /* WITH_SSL_SUPPORT */
         break; 
+      case TAG_SSL_DH_PARAMS_FILE:
+#ifdef WITH_SSL_SUPPORT
+        opt_p(&(opt.ssl_dh_params_file), optarg, &(opt.ssl_mask), 
+			MASK_SSL_DH_PARAMS_FILE, f);
+#else /* WITH_SSL_SUPPORT */
+	NO_SSL_OPT("ssl_dh_params_file");
+#endif /* WITH_SSL_SUPPORT */
+        break; 
       case TAG_SSL_CERT_ACCEPT_EXPIRED:
 #ifdef WITH_SSL_SUPPORT
         opt_i(&(opt.ssl_cert_accept_expired), 1, &(opt.ssl_mask),
@@ -1755,6 +1766,7 @@
     OPT_STR(RECOMMENDED_SSL_CA_FILE),
     OPT_STR(DEFAULT_SSL_CA_PATH),
     OPT_STR(DEFAULT_SSL_CERT_FILE),
+    OPT_STR(DEFAULT_SSL_DH_PARAMS_FILE),
     DEFAULT_SSL_CERT_VERIFY_DEPTH,
     OPT_STR(DEFAULT_SSL_KEY_FILE),
     OPT_STR(DEFAULT_SSL_LISTEN_CIPHERS),
diff -r 384a78e5951a perdition/options.h
--- a/perdition/options.h	Fri Oct 04 11:44:06 2013 +0900
+++ b/perdition/options.h	Fri Oct 25 17:07:36 2013 -0400
@@ -167,6 +167,7 @@
 #define DEFAULT_SSL_CA_ACCEPT_SELF_SIGNED    0
 #define DEFAULT_SSL_CERT_FILE                PERDITION_SYSCONFDIR \
 					     "/perdition.crt.pem"
+#define DEFAULT_SSL_DH_PARAMS_FILE           NULL
 #define DEFAULT_SSL_CERT_ACCEPT_EXPIRED      0
 #define DEFAULT_SSL_CERT_ACCEPT_SELF_SIGNED  0
 #define DEFAULT_SSL_CERT_ACCEPT_NOT_YET_VALID 0
@@ -236,6 +237,7 @@
   char            *ssl_ca_path;
   int             ssl_ca_accept_self_signed;
   char            *ssl_cert_file;
+  char            *ssl_dh_params_file;
   int             ssl_cert_accept_self_signed;
   int             ssl_cert_accept_expired;
   int             ssl_cert_accept_not_yet_valid;
@@ -317,6 +319,7 @@
 #define MASK_SSL_NO_CN_VERIFY                  (flag_t) 0x00004000
 #define MASK_SSL_PASSPHRASE_FD                 (flag_t) 0x00008000
 #define MASK_SSL_PASSPHRASE_FILE               (flag_t) 0x00010000
+#define MASK_SSL_DH_PARAMS_FILE                (flag_t) 0x00020000
 #endif /* WITH_SSL_SUPPORT */
 
 /* 
@@ -355,6 +358,7 @@
 #define TAG_MANAGESIEVE_CAPABILITY             (int) 155
 #define TAG_POP_CAPABILITY                     (int) 156
 #define TAG_TCP_KEEPALIVE                      (int) 157
+#define TAG_SSL_DH_PARAMS_FILE                 (int) 158
 
 /*Flag values for options()*/
 #define OPT_ERR         (flag_t) 0x1  /*Print error to stderr, enable help*/
diff -r 384a78e5951a perdition/perdition.8
--- a/perdition/perdition.8	Fri Oct 04 11:44:06 2013 +0900
+++ b/perdition/perdition.8	Fri Oct 25 17:07:36 2013 -0400
@@ -571,6 +571,12 @@
 .br
 (default "/etc/perdition/perdition.crt.pem")
 .TP 
+.B \-\-ssl_dh_params_file FILENAME:
+Diffie-Hellman parameters to use when offering EDH ciphersuites to clients.
+Should be in PEM format.
+.br
+(default: look for DH parameters in ssl_cert_file)
+.TP 
 .B \-\-ssl_cert_accept_self_signed:
 Accept self-signed certificates.
 Used for SSL or TLS outgoing connections.
diff -r 384a78e5951a perdition/perdition.c
--- a/perdition/perdition.c	Fri Oct 04 11:44:06 2013 +0900
+++ b/perdition/perdition.c	Fri Oct 25 17:07:36 2013 -0400
@@ -509,8 +509,8 @@
   if(opt.ssl_mode & SSL_LISTEN_MASK) {
     ssl_ctx = perdition_ssl_ctx(opt.ssl_ca_file, opt.ssl_ca_path,
 				opt.ssl_cert_file, opt.ssl_key_file,
-				opt.ssl_ca_chain_file, opt.ssl_listen_ciphers,
-				PERDITION_SSL_SERVER);
+				opt.ssl_ca_chain_file, opt.ssl_dh_params_file,
+				opt.ssl_listen_ciphers, PERDITION_SSL_SERVER);
     if(!ssl_ctx) {
       PERDITION_DEBUG_SSL_ERR("perdition_ssl_ctx");
       VANESSA_LOGGER_ERR("Fatal error establishing SSL context for listening");
diff -r 384a78e5951a perdition/ssl.c
--- a/perdition/ssl.c	Fri Oct 04 11:44:06 2013 +0900
+++ b/perdition/ssl.c	Fri Oct 25 17:07:36 2013 -0400
@@ -166,6 +166,11 @@
  *               concatenation of the various PEM-encoded CA Certificate 
  *               files, usually in certificate chain order.  
  *               Overrides ca_pat and ca_file
+ *      dh_params_file: Diffie-Hellman parameters to use as a server
+ *               May be NULL if not a server, if the DH params are
+ *               appended to the cert file, or if EDH ciphersuites are
+ *               not desired.  Should be the path to a PEM file that
+ *               contains DH PARAMETERS
  *      ciphers: cipher list to use as per ciphers(1). 
  *               May be NULL in which case openssl's default is used.
  *      flag: PERDITION_SSL_CLIENT or PERDITION_SSL_SERVER
@@ -488,9 +493,13 @@
 
 SSL_CTX *perdition_ssl_ctx(const char *ca_file, const char *ca_path, 
 		const char *cert, const char *privkey, 
-		const char *ca_chain_file, const char *ciphers, flag_t flag)
+		const char *ca_chain_file, const char *dh_params_file,
+		const char *ciphers, flag_t flag)
 {
 	SSL_CTX *ssl_ctx, *out = NULL;
+	const char *dhfile = NULL;
+	FILE* dhfp = NULL;
+	DH* dh = NULL;
 	const char *use_ca_file = NULL;
 	const char *use_ca_path = NULL;
 	struct passwd_cb_data pw_data;
@@ -533,6 +542,46 @@
 	}
 
 	/*
+	 * Load the Diffie-Hellman parameters:
+	 */
+	if (flag & PERDITION_SSL_SERVER &&
+		(dh_params_file || cert)) {
+		dhfile = (dh_params_file ? dh_params_file : cert);
+		dhfp = fopen(dhfile, "r");
+		if (dhfp == NULL) {
+			if (dh_params_file) {
+				VANESSA_LOGGER_ERR_UNSAFE
+					("Error opening Diffie-Hellman parameters file \"%s\"", dhfile);
+				SSL_CTX_free(ssl_ctx);
+				return NULL;
+			} else {
+				VANESSA_LOGGER_DEBUG_UNSAFE("could not open cert file for reading DH params \"%s\"", dhfile);
+			}
+		} else {
+			dh = PEM_read_DHparams(dhfp, NULL, NULL, NULL);
+			fclose(dhfp);
+			if (dh == NULL) {
+				if (dh_params_file) {
+					PERDITION_DEBUG_SSL_ERR("PEM_read_DHparams");
+					VANESSA_LOGGER_ERR_UNSAFE
+						("Error reading Diffie-Hellman parameters from file \"%s\"", dhfile);
+					SSL_CTX_free(ssl_ctx);
+					return NULL;
+				} else {
+					VANESSA_LOGGER_DEBUG_UNSAFE("could not read DH params from cert file \"%s\"", dhfile);
+				}
+			} else {
+				if (!SSL_CTX_set_tmp_dh(ssl_ctx, dh)) {
+					PERDITION_DEBUG_SSL_ERR("SSL_CTX_set_tmp_dh");
+					VANESSA_LOGGER_ERR_UNSAFE
+					("Error loading Diffie-Hellman parameters: \"%s\"", dhfile);
+				}
+				DH_free(dh);
+			}
+		}
+	}		  
+
+	/*
 	 * Set the available ciphers
 	 */
 	if(ciphers && SSL_CTX_set_cipher_list(ssl_ctx, ciphers) < 0) {
@@ -1154,7 +1203,7 @@
 	io_t *new_io;
 
 	ssl_ctx = perdition_ssl_ctx(ca_file, ca_path, NULL, NULL, NULL,
-			ciphers, PERDITION_SSL_CLIENT);
+			NULL, ciphers, PERDITION_SSL_CLIENT);
 	if (!ssl_ctx) {
 		PERDITION_DEBUG_SSL_ERR("perdition_ssl_ctx");
 		io_destroy(io);
diff -r 384a78e5951a perdition/ssl.h
--- a/perdition/ssl.h	Fri Oct 04 11:44:06 2013 +0900
+++ b/perdition/ssl.h	Fri Oct 25 17:07:36 2013 -0400
@@ -62,6 +62,11 @@
  *               the root CA certificate. Such a file is simply the
  *               concatenation of the various PEM-encoded CA Certificate 
  *               files, usually in certificate chain order.  
+ *      dh_params_file: Diffie-Hellman parameters to use as a server
+ *               May be NULL if not a server, if the DH params are
+ *               appended to the cert file, or if EDH ciphersuites are
+ *               not desired.  Should be the path to a PEM file that
+ *               contains DH PARAMETERS
  *      ciphers: cipher list to use as per ciphers(1). 
  *               May be NULL in which case openssl's default is used.
  *      flag: PERDITION_SSL_CLIENT or PERDITION_SSL_SERVER
@@ -79,7 +84,8 @@
 
 SSL_CTX *perdition_ssl_ctx(const char *ca_file, const char *ca_path,
 		const char *cert, const char *privkey, 
-		const char *ca_chain_file, const char *ciphers, flag_t flag);
+		const char *ca_chain_file, const char *dh_params_file,
+		const char *ciphers, flag_t flag);
 
 
 /**********************************************************************
signature.asc (application/pgp-signature, 965 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.15 (GNU/Linux)

iQJ8BAEBCgBmBQJSauDKXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w
ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRFQjk2OTEyODdBN0FEREUzNzU3RDkxMUVB
NTI0MDFCMTFCRkRGQTVDAAoJEKUkAbEb/fpc9gwP/j2+uVDa8RTrnA5ZNkZUdkCP
5SYIVxxpOMe34DHW4aJSu600VtNaZZKT16xRvJMSyFMXPCs3mXIZmUBz0YVnLXM4
4LWLJroG5yeEfmZdrz03FieIqjjFQLA3U7MV6IH3OVcFkRYovCrqydy8KkQRg/ny
sTn5yJSSHYq3JX6JCvWTrJgdvjh7dTJHaLJYJCwzBGJc3wyoIlMU3IxfbEv0bFqY
CvCzJJY2qQ/vH9W7TZsnzsFftoDwR3h45hLJi80l0l5vk5kws6td5a0x5/GkpDHF
xIFTiSI2EKu58AkhM6nRRu6ytDFWs0G6vZ83KtFCsmomZE1wG/5GZlcs6//SCxn4
DdUzuDBXxfTD0s8+G/2wVbniSgvqi1ohqM3JVhr8JZ9Aa6/ei9cRJNY+Us4qRPHW
oscu6Srrm/6ix1t6sWcZwkZBR0yKkmKKZICGC9ByV4f3nX7hv2W+FN/Q5jjhXbHc
qXs1chYHNWR0B+4NtnLii9xK6rxlNEvi7Lj2v/DZxgsv4rf0x3aL8NhHtAYHhEfE
rc1lw1NWqEPi3maIwEVGWnkZtSXpzZeWW4KZDygSZT8ObrCm3sETBND8Umwgxi7y
L2U9eumDLuUtXnxhDFtETn3B/pcg0cLcpHCOrSHfrwZWiqsX+WP12J57hDqfkMEL
43wmTgoJDOwCsq1vuAfZ
=gta+
-----END PGP SIGNATURE-----