# HG changeset patch # User Timo Sirainen # Date 1254952021 14400 # Node ID ea36bad4d9dab8a04aecb08da4558e19cb380309 # Parent 097588a7903c7d973df5e8207154ac13727032a2 Added ssl-params binary that login process uses to read SSL parameters. diff -r 097588a7903c -r ea36bad4d9da configure.in --- a/configure.in Wed Oct 07 17:46:14 2009 -0400 +++ b/configure.in Wed Oct 07 17:47:01 2009 -0400 @@ -2507,6 +2507,7 @@ src/master/Makefile src/pop3/Makefile src/pop3-login/Makefile +src/ssl-params/Makefile src/util/Makefile src/plugins/Makefile src/plugins/acl/Makefile diff -r 097588a7903c -r ea36bad4d9da doc/example-config/conf.d/master.conf --- a/doc/example-config/conf.d/master.conf Wed Oct 07 17:46:14 2009 -0400 +++ b/doc/example-config/conf.d/master.conf Wed Oct 07 17:47:01 2009 -0400 @@ -153,3 +153,12 @@ mode = 0666 } } + +service ssl-params { + executable = ssl-params + + unix_listener { + path = login/ssl-params + mode = 0666 + } +} diff -r 097588a7903c -r ea36bad4d9da src/Makefile.am --- a/src/Makefile.am Wed Oct 07 17:46:14 2009 -0400 +++ b/src/Makefile.am Wed Oct 07 17:47:01 2009 -0400 @@ -33,4 +33,5 @@ config \ util \ dsync \ + ssl-params \ plugins diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/Makefile.am Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,21 @@ +pkglibexecdir = $(libexecdir)/dovecot + +pkglibexec_PROGRAMS = ssl-params + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-settings \ + -DPKG_STATEDIR=\""$(statedir)"\" + +ssl_params_LDADD = $(LIBDOVECOT) $(SSL_LIBS) +ssl_params_DEPENDENCIES = $(LIBDOVECOT) $(SSL_LIBS) +ssl_params_SOURCES = \ + main.c \ + ssl-params.c \ + ssl-params-openssl.c \ + ssl-params-settings.c + +noinst_HEADERS = \ + ssl-params.h \ + ssl-params-settings.h diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/main.c Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,136 @@ +/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "lib-signals.h" +#include "array.h" +#include "ostream.h" +#include "master-service.h" +#include "ssl-params-settings.h" +#include "ssl-params.h" + +#include +#include +#include + +#define SSL_BUILD_PARAM_FNAME "ssl-parameters.dat" + +struct client { + int fd; + struct ostream *output; +}; + +static ARRAY_DEFINE(delayed_fds, int); +struct ssl_params *param; +static buffer_t *ssl_params; + +static int client_output_flush(struct ostream *output) +{ + if (o_stream_flush(output) == 0) { + /* more to come */ + return 0; + } + /* finished / disconnected */ + o_stream_destroy(&output); + return -1; +} + +static void client_handle(int fd) +{ + struct ostream *output; + + output = o_stream_create_fd(fd, (size_t)-1, TRUE); + o_stream_send(output, ssl_params->data, ssl_params->used); + + if (o_stream_get_buffer_used_size(output) == 0) + o_stream_destroy(&output); + else { + o_stream_set_flush_callback(output, client_output_flush, + output); + } +} + +static void client_connected(const struct master_service_connection *conn) +{ + if (ssl_params->used == 0) { + /* waiting for parameter building to finish */ + if (!array_is_created(&delayed_fds)) + i_array_init(&delayed_fds, 32); + array_append(&delayed_fds, &conn->fd, 1); + } + client_handle(conn->fd); +} + +static void ssl_params_callback(const unsigned char *data, size_t size) +{ + const int *fds; + unsigned int i, count; + + buffer_set_used_size(ssl_params, 0); + buffer_append(ssl_params, data, size); + + if (!array_is_created(&delayed_fds)) + return; + + fds = array_get(&delayed_fds, &count); + for (i = 0; i < count; i++) + client_handle(fds[i]); + array_free(&delayed_fds); +} + +static void sig_chld(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) +{ + int status; + + if (waitpid(-1, &status, WNOHANG) < 0) + i_error("waitpid() failed: %m"); + else if (status != 0) + i_error("child process failed with status %d", status); + else { + /* params should have been created now. try refreshing. */ + ssl_params_refresh(param); + } +} + +static void main_init(const struct ssl_params_settings *set) +{ + lib_signals_set_handler(SIGCHLD, TRUE, sig_chld, NULL); + + ssl_params = buffer_create_dynamic(default_pool, 1024); + param = ssl_params_init(PKG_STATEDIR"/"SSL_BUILD_PARAM_FNAME, + ssl_params_callback, set); +} + +static void main_deinit(void) +{ + ssl_params_deinit(¶m); + if (array_is_created(&delayed_fds)) + array_free(&delayed_fds); +} + +int main(int argc, char *argv[]) +{ + const struct ssl_params_settings *set; + int c; + + master_service = master_service_init("ssl-build-param", 0, argc, argv); + master_service_init_log(master_service, "ssl-build-param: "); + + while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) { + if (!master_service_parse_option(master_service, c, optarg)) + exit(FATAL_DEFAULT); + } + + set = ssl_params_settings_read(master_service); + master_service_init_finish(master_service); + +#ifndef HAVE_SSL + i_fatal("Dovecot built without SSL support"); +#endif + + main_init(set); + master_service_run(master_service, client_connected); + main_deinit(); + + master_service_deinit(&master_service); + return 0; +} diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/ssl-params Binary file src/ssl-params/ssl-params has changed diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/ssl-params-openssl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/ssl-params-openssl.c Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,71 @@ +/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "write-full.h" +#include "ssl-params.h" + +#ifdef HAVE_OPENSSL + +#include +#include + +/* 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 }; + +static const char *ssl_last_error(void) +{ + unsigned long err; + char *buf; + size_t err_size = 256; + + err = ERR_get_error(); + if (err == 0) + return strerror(errno); + + buf = t_malloc(err_size); + buf[err_size-1] = '\0'; + ERR_error_string_n(err, buf, err_size-1); + return buf; +} + +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; + + if (dh == NULL) { + i_fatal("DH_generate_parameters(bits=%d, gen=%d) failed: %s", + bitsize, DH_GENERATOR, ssl_last_error()); + } + + len = i2d_DHparams(dh, NULL); + if (len < 0) + i_fatal("i2d_DHparams() failed: %s", ssl_last_error()); + + 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 < N_ELEMENTS(dh_param_bitsizes); i++) + generate_dh_parameters(dh_param_bitsizes[i], fd, fname); + bits = 0; + if (write_full(fd, &bits, sizeof(bits)) < 0) + i_fatal("write_full() failed for file %s: %m", fname); +} + +#endif diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/ssl-params-settings.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/ssl-params-settings.c Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,53 @@ +/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "master-service-settings.h" +#include "ssl-params-settings.h" + +#include +#include +#include + +#undef DEF +#define DEF(type, name) \ + { type, #name, offsetof(struct ssl_params_settings, name), NULL } + +static struct setting_define ssl_params_setting_defines[] = { + DEF(SET_UINT, ssl_parameters_regenerate), + + SETTING_DEFINE_LIST_END +}; + +static struct ssl_params_settings ssl_params_default_settings = { + MEMBER(ssl_parameters_regenerate) 24*7 +}; + +struct setting_parser_info ssl_params_setting_parser_info = { + MEMBER(defines) ssl_params_setting_defines, + MEMBER(defaults) &ssl_params_default_settings, + + MEMBER(parent) NULL, + MEMBER(dynamic_parsers) NULL, + + MEMBER(parent_offset) (size_t)-1, + MEMBER(type_offset) (size_t)-1, + MEMBER(struct_size) sizeof(struct ssl_params_settings) +}; + +struct ssl_params_settings * +ssl_params_settings_read(struct master_service *service) +{ + static const struct setting_parser_info *set_roots[] = { + &ssl_params_setting_parser_info, + NULL + }; + const char *error; + void **sets; + + if (master_service_settings_read_simple(service, set_roots, &error) < 0) + i_fatal("Error reading configuration: %s", error); + + sets = master_service_settings_get_others(service); + return sets[0]; +} diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/ssl-params-settings.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/ssl-params-settings.h Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,13 @@ +#ifndef SSL_PARAMS_SETTINGS_H +#define SSL_PARAMS_SETTINGS_H + +struct master_service; + +struct ssl_params_settings { + unsigned int ssl_parameters_regenerate; +}; + +struct ssl_params_settings * +ssl_params_settings_read(struct master_service *service); + +#endif diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/ssl-params.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/ssl-params.c Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,208 @@ +/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "buffer.h" +#include "file-lock.h" +#include "read-full.h" +#include "master-service-settings.h" +#include "ssl-params-settings.h" +#include "ssl-params.h" + +#include +#include +#include +#include +#include + +#define MAX_PARAM_FILE_SIZE 1024 +#define SSL_BUILD_PARAM_TIMEOUT_SECS (60*30) + +struct ssl_params { + char *path; + 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) +{ + const char *temp_path; + struct file_lock *lock; + struct stat st, st2; + mode_t old_mask; + int fd, ret; + + temp_path = t_strconcat(path, ".tmp", NULL); + + old_mask = umask(0); + fd = open(temp_path, O_WRONLY | O_CREAT, 0644); + umask(old_mask); + + if (fd == -1) + i_fatal("creat(%s) failed: %m", temp_path); + + /* If multiple dovecot instances are running, only one of them needs + to regenerate this file. */ + ret = file_wait_lock(fd, temp_path, F_WRLCK, + FILE_LOCK_METHOD_FCNTL, + SSL_BUILD_PARAM_TIMEOUT_SECS, &lock); + if (ret < 0) + i_fatal("file_try_lock(%s) failed: %m", temp_path); + if (ret == 0) { + /* someone else is writing this */ + i_fatal("Timeout while waiting for %s generation to complete", + path); + return; + } + + /* make sure the .tmp file is still the one we created */ + if (fstat(fd, &st) < 0) + i_fatal("fstat(%s) failed: %m", temp_path); + if (stat(temp_path, &st2) < 0) + i_fatal("stat(%s) failed: %m", temp_path); + if (st.st_ino != st2.st_ino) { + /* nope. so someone else just generated the file. */ + (void)close(fd); + return; + } + + /* check that the parameters file is still the same */ + if (stat(path, &st) < 0) + i_fatal("stat(%s) failed: %m", temp_path); + if (st.st_mtime != mtime) { + (void)close(fd); + return; + } + + /* ok, we really want to generate it. */ + if (ftruncate(fd, 0) < 0) + i_fatal("ftruncate(%s) failed: %m", temp_path); + + i_info("Generating SSL parameters"); +#ifdef HAVE_SSL + ssl_generate_parameters(fd, temp_path); +#endif + + if (rename(temp_path, path) < 0) + i_fatal("rename(%s, %s) failed: %m", temp_path, path); + if (close(fd) < 0) + i_fatal("close(%s) failed: %m", temp_path); + file_lock_free(&lock); + + i_info("SSL parameters regeneration completed"); +} + +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"); + case 0: + /* child */ + ssl_params_if_unchanged(param->path, param->last_mtime); + exit(0); + default: + /* parent */ + break; + } +} + +static void ssl_params_set_timeout(struct ssl_params *param) +{ + time_t next_rebuild, diff; + + if (param->to_rebuild != NULL) + timeout_remove(¶m->to_rebuild); + + next_rebuild = param->last_mtime + + param->set.ssl_parameters_regenerate * 3600; + + if (ioloop_time >= next_rebuild) { + ssl_params_rebuild(param); + return; + } + + 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); +} + +static int ssl_params_read(struct ssl_params *param) +{ + unsigned char *buffer; + struct stat st; + int fd, ret; + + fd = open(param->path, O_RDONLY); + if (fd == -1) { + if (errno != ENOENT) + i_error("open(%s) failed: %m", param->path); + return -1; + } + + if (fstat(fd, &st) < 0) { + i_error("stat(%s) failed: %m", param->path); + (void)close(fd); + return -1; + } + if (st.st_size == 0 || st.st_size > MAX_PARAM_FILE_SIZE) { + i_error("Corrupted file: %s", param->path); + (void)unlink(param->path); + return -1; + } + + buffer = t_malloc(st.st_size); + ret = read_full(fd, buffer, st.st_size); + if (ret < 0) + i_error("read(%s) failed: %m", param->path); + else if (ret == 0) { + i_error("File unexpectedly shrank: %s", param->path); + ret = -1; + } else { + param->last_mtime = st.st_mtime; + ssl_params_set_timeout(param); + param->callback(buffer, st.st_size); + } + + if (close(fd) < 0) + i_error("close(%s) failed: %m", param->path); + return ret; +} + +struct ssl_params * +ssl_params_init(const char *path, ssl_params_callback_t *callback, + const struct ssl_params_settings *set) +{ + struct ssl_params *param; + + param = i_new(struct ssl_params, 1); + param->path = i_strdup(path); + param->set = *set; + param->callback = callback; + ssl_params_refresh(param); + return param; +} + +void ssl_params_refresh(struct ssl_params *param) +{ + if (ssl_params_read(param) < 0) + ssl_params_rebuild(param); +} + +void ssl_params_deinit(struct ssl_params **_param) +{ + struct ssl_params *param = *_param; + + *_param = NULL; + if (param->to_rebuild != NULL) + timeout_remove(¶m->to_rebuild); + i_free(param->path); + i_free(param); +} diff -r 097588a7903c -r ea36bad4d9da src/ssl-params/ssl-params.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssl-params/ssl-params.h Wed Oct 07 17:47:01 2009 -0400 @@ -0,0 +1,17 @@ +#ifndef SSL_BUILD_PARAMS_H +#define SSL_BUILD_PARAMS_H + +struct ssl_params_settings; + +typedef void ssl_params_callback_t(const unsigned char *data, size_t size); + +struct ssl_params * +ssl_params_init(const char *path, ssl_params_callback_t *callback, + const struct ssl_params_settings *set); +void ssl_params_deinit(struct ssl_params **param); + +void ssl_params_refresh(struct ssl_params *param); + +void ssl_generate_parameters(int fd, const char *fname); + +#endif