# auth_ssl_username_from_cert=yes.
#ssl_cert_username_field = commonName
-# 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 = 168
+# DH parameters length to use.
+#ssl_dh_parameters_length = 1024
# SSL protocols to use
#ssl_protocols = !SSLv2
time_t last_refresh;
int fd;
- DH *dh_512, *dh_1024;
+ DH *dh_512, *dh_default;
};
struct ssl_server_context {
return ctx1->verify_client_cert == ctx2->verify_client_cert ? 0 : 1;
}
-static void ssl_params_corrupted(void)
+static void ssl_params_corrupted(const char *reason)
{
- i_fatal("Corrupted SSL parameters file in state_dir: ssl-parameters.dat");
+ i_fatal("Corrupted SSL ssl-parameters.dat in state_dir: %s", reason);
}
static void read_next(struct ssl_parameters *params, void *data, size_t size)
if ((ret = read_full(params->fd, data, size)) < 0)
i_fatal("read(%s) failed: %m", params->path);
if (ret == 0)
- ssl_params_corrupted();
+ ssl_params_corrupted("Truncated file");
}
static bool read_dh_parameters_next(struct ssl_parameters *params)
/* read data size. */
read_next(params, &len, sizeof(len));
if (len > 1024*100) /* should be enough? */
- ssl_params_corrupted();
+ ssl_params_corrupted("File too large");
buf = i_malloc(len);
read_next(params, buf, len);
cbuf = buf;
switch (bits) {
case 512:
+ if (params->dh_512 != NULL)
+ ssl_params_corrupted("Duplicate 512bit parameters");
params->dh_512 = d2i_DHparams(NULL, &cbuf, len);
break;
- case 1024:
- params->dh_1024 = d2i_DHparams(NULL, &cbuf, len);
- break;
default:
- ssl_params_corrupted();
+ if (params->dh_default != NULL)
+ ssl_params_corrupted("Duplicate default parameters");
+ params->dh_default = d2i_DHparams(NULL, &cbuf, len);
+ break;
}
i_free(buf);
DH_free(params->dh_512);
params->dh_512 = NULL;
}
- if (params->dh_1024 != NULL) {
- DH_free(params->dh_1024);
- params->dh_1024 = NULL;
+ if (params->dh_default != NULL) {
+ DH_free(params->dh_default);
+ params->dh_default = NULL;
}
}
i_fatal("read(%s) failed: %m", params->path);
else if (ret != 0) {
/* more data than expected */
- ssl_params_corrupted();
+ ssl_params_corrupted("More data than expected");
}
if (close(params->fd) < 0)
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;
+ return ssl_params.dh_default;
}
static void ssl_info_callback(const SSL *ssl, int where, int ret)
default.. */
#define DH_GENERATOR 2
-static int dh_param_bitsizes[] = { 512, 1024 };
-
static const char *ssl_last_error(void)
{
unsigned long err;
i_free(buf);
}
-void ssl_generate_parameters(int fd, const char *fname)
+void ssl_generate_parameters(int fd, unsigned int dh_length, const char *fname)
{
- unsigned int i;
int bits;
- for (i = 0; i < N_ELEMENTS(dh_param_bitsizes); i++)
- generate_dh_parameters(dh_param_bitsizes[i], fd, fname);
+ generate_dh_parameters(512, fd, fname);
+ generate_dh_parameters(dh_length, fd, fname);
bits = 0;
if (write_full(fd, &bits, sizeof(bits)) < 0)
i_fatal("write_full() failed for file %s: %m", fname);
static const struct setting_define ssl_params_setting_defines[] = {
DEF(SET_TIME, ssl_parameters_regenerate),
+ DEF(SET_UINT, ssl_dh_parameters_length),
SETTING_DEFINE_LIST_END
};
static const struct ssl_params_settings ssl_params_default_settings = {
- .ssl_parameters_regenerate = 3600*24*7
+ .ssl_parameters_regenerate = 0,
+ .ssl_dh_parameters_length = 1024
};
const struct setting_parser_info ssl_params_setting_parser_info = {
struct ssl_params_settings {
unsigned int ssl_parameters_regenerate;
+ unsigned int ssl_dh_parameters_length;
};
struct ssl_params_settings *
# include <sys/resource.h>
#endif
-#define MAX_PARAM_FILE_SIZE 1024
+#define MAX_PARAM_FILE_SIZE 1024*1024
#define SSL_BUILD_PARAM_TIMEOUT_SECS (60*30)
#define SSL_PARAMS_PRIORITY 15
struct ssl_params_settings set;
time_t last_mtime;
- struct timeout *to_rebuild;
ssl_params_callback_t *callback;
};
-static void ssl_params_if_unchanged(const char *path, time_t mtime)
+static void ssl_params_if_unchanged(const char *path, time_t mtime,
+ unsigned int ssl_dh_parameters_length)
{
const char *temp_path;
struct file_lock *lock;
i_info("Generating SSL parameters");
#ifdef HAVE_SSL
- ssl_generate_parameters(fd, temp_path);
+ ssl_generate_parameters(fd, ssl_dh_parameters_length, temp_path);
#endif
if (rename(temp_path, path) < 0)
static void ssl_params_rebuild(struct ssl_params *param)
{
- if (param->to_rebuild != NULL)
- timeout_remove(¶m->to_rebuild);
-
switch (fork()) {
case -1:
i_fatal("fork() failed: %m");
/* child - close listener fds so a long-running ssl-params
doesn't cause Dovecot restart to fail */
ssl_params_close_listeners();
- ssl_params_if_unchanged(param->path, param->last_mtime);
+ ssl_params_if_unchanged(param->path, param->last_mtime,
+ param->set.ssl_dh_parameters_length);
exit(0);
default:
/* parent */
}
}
-static void ssl_params_set_timeout(struct ssl_params *param)
+static bool
+ssl_params_verify(struct ssl_params *param,
+ const unsigned char *data, size_t size)
{
- time_t next_rebuild, diff;
-
- if (param->to_rebuild != NULL)
- timeout_remove(¶m->to_rebuild);
- if (param->set.ssl_parameters_regenerate == 0)
- return;
-
- next_rebuild = param->last_mtime +
- param->set.ssl_parameters_regenerate;
-
- if (ioloop_time >= next_rebuild) {
- ssl_params_rebuild(param);
- return;
+ unsigned int bitsize, len;
+ bool found = FALSE;
+
+ /* <bitsize><length><data>... */
+ while (size >= sizeof(bitsize)) {
+ memcpy(&bitsize, data, sizeof(bitsize));
+ if (bitsize == 0) {
+ if (found)
+ return TRUE;
+ i_warning("Regenerating %s for ssl_dh_parameters_length=%u",
+ param->path, param->set.ssl_dh_parameters_length);
+ return FALSE;
+ }
+ data += sizeof(bitsize);
+ size -= sizeof(bitsize);
+ if (bitsize == param->set.ssl_dh_parameters_length)
+ found = TRUE;
+
+ if (size < sizeof(len))
+ break;
+ memcpy(&len, data, sizeof(len));
+ if (len > size - sizeof(len))
+ break;
+ data += sizeof(bitsize) + len;
+ size -= sizeof(bitsize) + len;
}
-
- diff = next_rebuild - ioloop_time;
- if (diff > INT_MAX / 1000)
- diff = INT_MAX / 1000;
- param->to_rebuild = timeout_add(diff * 1000, ssl_params_rebuild, param);
+ i_error("Corrupted %s", param->path);
+ return FALSE;
}
static int ssl_params_read(struct ssl_params *param)
i_close_fd(&fd);
return -1;
}
+ param->last_mtime = st.st_mtime;
if (st.st_size == 0 || st.st_size > MAX_PARAM_FILE_SIZE) {
i_error("Corrupted file: %s", param->path);
i_close_fd(&fd);
else if (ret == 0) {
i_error("File unexpectedly shrank: %s", param->path);
ret = -1;
+ } else if (!ssl_params_verify(param, buffer, st.st_size)) {
+ ret = -1;
} else {
- param->last_mtime = st.st_mtime;
- ssl_params_set_timeout(param);
param->callback(buffer, st.st_size);
}
struct ssl_params *param = *_param;
*_param = NULL;
- if (param->to_rebuild != NULL)
- timeout_remove(¶m->to_rebuild);
i_free(param->path);
i_free(param);
}
void ssl_params_refresh(struct ssl_params *param);
-void ssl_generate_parameters(int fd, const char *fname);
+void ssl_generate_parameters(int fd, unsigned int dh_length, const char *fname);
#endif