Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3888:650701d41cdf HEAD
Generate DH parameters and use them. Changed default regeneration time to 1
week.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 15 Jan 2006 14:35:01 +0200 |
parents | f95146a34af9 |
children | c7462001227b |
files | dovecot-example.conf src/login-common/ssl-proxy-openssl.c src/master/master-settings.c src/master/ssl-init-openssl.c src/master/ssl-init.c |
diffstat | 5 files changed, 192 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Sun Jan 15 14:15:12 2006 +0200 +++ b/dovecot-example.conf Sun Jan 15 14:35:01 2006 +0200 @@ -47,13 +47,13 @@ #ssl_verify_client_cert = no # SSL parameter file. Master process generates this file for login processes. -# It contains Diffie Hellman and RSA parameters. +# It contains Diffie Hellman parameters. #ssl_parameters_file = /var/run/dovecot/ssl-parameters.dat # How often to regenerate the SSL parameters file. Generation is quite CPU # intensive operation. The value is in hours, 0 disables regeneration # entirely. -#ssl_parameters_regenerate = 24 +#ssl_parameters_regenerate = 168 # SSL ciphers to use #ssl_cipher_list = ALL:!LOW
--- a/src/login-common/ssl-proxy-openssl.c Sun Jan 15 14:15:12 2006 +0200 +++ b/src/login-common/ssl-proxy-openssl.c Sun Jan 15 14:35:01 2006 +0200 @@ -1,11 +1,17 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "common.h" +#include "array.h" #include "ioloop.h" #include "network.h" +#include "read-full.h" #include "hash.h" #include "ssl-proxy.h" +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + #ifdef HAVE_OPENSSL #include <openssl/crypto.h> @@ -16,6 +22,8 @@ #include <openssl/rand.h> #define DOVECOT_SSL_DEFAULT_CIPHER_LIST "ALL:!LOW" +/* Check every 30 minutes if parameters file has been updated */ +#define SSL_PARAMFILE_CHECK_INTERVAL (60*30) enum ssl_io_action { SSL_ADD_INPUT, @@ -45,9 +53,18 @@ unsigned int cert_broken:1; }; +struct ssl_parameters { + const char *fname; + time_t last_mtime; + int fd; + + DH *dh_512, *dh_1024; +}; + static int extdata_index; static SSL_CTX *ssl_ctx; static struct hash_table *ssl_proxies; +static struct ssl_parameters ssl_params; static void plain_read(void *context); static void plain_write(void *context); @@ -56,6 +73,111 @@ static void ssl_proxy_destroy(struct ssl_proxy *proxy); static void ssl_proxy_unref(struct ssl_proxy *proxy); +static void read_next(struct ssl_parameters *params, void *data, size_t size) +{ + int ret; + + if ((ret = read_full(params->fd, data, size)) < 0) + i_fatal("read(%s) failed: %m", params->fname); + if (ret == 0) + i_fatal("read(%s) failed: Unexpected EOF", params->fname); +} + +static bool read_dh_parameters_next(struct ssl_parameters *params) +{ + unsigned char *buf; + const unsigned char *cbuf; + unsigned int len; + int bits; + + /* read bit size. 0 ends the DH parameters list. */ + read_next(params, &bits, sizeof(bits)); + + if (bits == 0) + return FALSE; + + /* read data size. */ + read_next(params, &len, sizeof(len)); + if (len > 1024*100) /* should be enough? */ + i_fatal("Corrupted SSL parameters file: %s", params->fname); + + buf = i_malloc(len); + read_next(params, buf, len); + + cbuf = buf; + switch (bits) { + case 512: + params->dh_512 = d2i_DHparams(NULL, &cbuf, len); + break; + case 1024: + params->dh_1024 = d2i_DHparams(NULL, &cbuf, len); + break; + } + + i_free(buf); + return TRUE; +} + +static void ssl_free_parameters(struct ssl_parameters *params) +{ + if (params->dh_512 != NULL) { + DH_free(params->dh_512); + params->dh_512 = NULL; + } + if (params->dh_1024 != NULL) { + DH_free(params->dh_1024); + params->dh_1024 = NULL; + } +} + +static void ssl_read_parameters(struct ssl_parameters *params) +{ + bool warned = FALSE; + + /* we'll wait until parameter file exists */ + for (;;) { + params->fd = open(params->fname, O_RDONLY); + if (params->fd != -1) + break; + + if (errno != ENOENT) { + i_fatal("Can't open SSL parameter file %s: %m", + params->fname); + } + + if (!warned) { + i_warning("Waiting for SSL parameter file %s", + params->fname); + warned = TRUE; + } + sleep(1); + } + + ssl_free_parameters(params); + while (read_dh_parameters_next(params)) ; + + if (close(params->fd) < 0) + i_error("close() failed: %m"); + params->fd = -1; +} + +static void ssl_refresh_parameters(struct ssl_parameters *params) +{ + struct stat st; + + if (params->last_mtime > ioloop_time - SSL_PARAMFILE_CHECK_INTERVAL) + return; + + if (params->last_mtime == 0) + ssl_read_parameters(params); + else { + if (stat(params->fname, &st) < 0) + i_error("stat(%s) failed: %m", params->fname); + else if (st.st_mtime != params->last_mtime) + ssl_read_parameters(params); + } +} + static void ssl_set_io(struct ssl_proxy *proxy, enum ssl_io_action action) { switch (action) { @@ -327,6 +449,8 @@ return -1; } + ssl_refresh_parameters(&ssl_params); + ssl = SSL_new(ssl_ctx); if (ssl == NULL) { i_error("SSL_new() failed: %s", ssl_last_error()); @@ -437,6 +561,17 @@ return RSA_generate_key(keylength, RSA_F4, NULL, NULL); } +static DH *ssl_tmp_dh_callback(SSL *ssl __attr_unused__, + int is_export, int keylength) +{ + /* Well, I'm not exactly sure why the logic in here is this. + It's the same as in Postfix, so it can't be too wrong. */ + if (is_export && keylength == 512 && ssl_params.dh_512 != NULL) + return ssl_params.dh_512; + + return ssl_params.dh_1024; +} + static int ssl_verify_client_cert(int preverify_ok, X509_STORE_CTX *ctx) { SSL *ssl; @@ -455,15 +590,17 @@ void ssl_proxy_init(void) { - const char *cafile, *certfile, *keyfile, *paramfile, *cipher_list; + const char *cafile, *certfile, *keyfile, *cipher_list; unsigned char buf; + memset(&ssl_params, 0, sizeof(ssl_params)); + cafile = getenv("SSL_CA_FILE"); certfile = getenv("SSL_CERT_FILE"); keyfile = getenv("SSL_KEY_FILE"); - paramfile = getenv("SSL_PARAM_FILE"); + ssl_params.fname = getenv("SSL_PARAM_FILE"); - if (certfile == NULL || keyfile == NULL || paramfile == NULL) { + if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) { /* SSL support is disabled */ return; } @@ -506,6 +643,7 @@ if (SSL_CTX_need_tmp_RSA(ssl_ctx)) SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key); + SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback); if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) { SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | @@ -537,6 +675,7 @@ hash_iterate_deinit(iter); hash_destroy(ssl_proxies); + ssl_free_parameters(&ssl_params); SSL_CTX_free(ssl_ctx); }
--- a/src/master/master-settings.c Sun Jan 15 14:15:12 2006 +0200 +++ b/src/master/master-settings.c Sun Jan 15 14:35:01 2006 +0200 @@ -257,7 +257,7 @@ MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem", MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem", MEMBER(ssl_parameters_file) "ssl-parameters.dat", - MEMBER(ssl_parameters_regenerate) 24, + MEMBER(ssl_parameters_regenerate) 168, MEMBER(ssl_cipher_list) NULL, MEMBER(ssl_verify_client_cert) FALSE, MEMBER(disable_plaintext_auth) TRUE,
--- a/src/master/ssl-init-openssl.c Sun Jan 15 14:15:12 2006 +0200 +++ b/src/master/ssl-init-openssl.c Sun Jan 15 14:35:01 2006 +0200 @@ -1,13 +1,50 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "common.h" +#include "write-full.h" #include "ssl-init.h" #ifdef HAVE_OPENSSL -void _ssl_generate_parameters(int fd __attr_unused__, - const char *fname __attr_unused__) +#include <openssl/ssl.h> + +/* 2 or 5. Haven't seen their difference explained anywhere, but 2 is the + default.. */ +#define DH_GENERATOR 2 + +static int dh_param_bitsizes[] = { 512, 1024 }; +#define DH_PARAM_BITSIZE_COUNT \ + (sizeof(dh_param_bitsizes)/sizeof(dh_param_bitsizes[0])) + +static void generate_dh_parameters(int bitsize, int fd, const char *fname) { + DH *dh = DH_generate_parameters(bitsize, DH_GENERATOR, NULL, NULL); + unsigned char *buf, *p; + int len; + + len = i2d_DHparams(dh, NULL); + if (len < 0) + i_fatal("i2d_DHparams() failed"); + + buf = p = i_malloc(len); + len = i2d_DHparams(dh, &p); + + if (write_full(fd, &bitsize, sizeof(bitsize)) < 0 || + write_full(fd, &len, sizeof(len)) < 0 || + write_full(fd, buf, len) < 0) + i_fatal("write_full() failed for file %s: %m", fname); + i_free(buf); +} + +void _ssl_generate_parameters(int fd, const char *fname) +{ + unsigned int i; + int bits; + + for (i = 0; i < DH_PARAM_BITSIZE_COUNT; i++) + generate_dh_parameters(dh_param_bitsizes[i], fd, fname); + bits = 0; + write_full(fd, &bits, sizeof(bits)); } #endif
--- a/src/master/ssl-init.c Sun Jan 15 14:15:12 2006 +0200 +++ b/src/master/ssl-init.c Sun Jan 15 14:35:01 2006 +0200 @@ -18,12 +18,16 @@ static void generate_parameters_file(const char *fname) { const char *temp_fname; + mode_t old_mask; int fd; temp_fname = t_strconcat(fname, ".tmp", NULL); (void)unlink(temp_fname); - fd = open(temp_fname, O_WRONLY | O_CREAT | O_EXCL, 0600); + old_mask = umask(0); + fd = open(temp_fname, O_WRONLY | O_CREAT | O_EXCL, 0644); + umask(old_mask); + if (fd == -1) { i_fatal("Can't create temporary SSL parameters file %s: %m", temp_fname); @@ -82,10 +86,11 @@ st.st_mtime = 0; } - /* make sure it's new enough and the permissions are correct */ + /* make sure it's new enough, it's not 0 sized, and the permissions + are correct */ regen_time = st.st_mtime + (time_t)(set->ssl_parameters_regenerate*3600); - if (regen_time < ioloop_time || (st.st_mode & 077) != 0 || + if (regen_time < ioloop_time || st.st_size == 0 || st.st_uid != master_uid || st.st_gid != getegid()) { start_generate_process(set); return FALSE;