src/master/Makefile
src/pop3/Makefile
src/pop3-login/Makefile
+src/ssl-params/Makefile
src/util/Makefile
src/plugins/Makefile
src/plugins/acl/Makefile
mode = 0666
}
}
+
+service ssl-params {
+ executable = ssl-params
+
+ unix_listener {
+ path = login/ssl-params
+ mode = 0666
+ }
+}
config \
util \
dsync \
+ ssl-params \
plugins
--- /dev/null
+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
--- /dev/null
+/* 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 <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#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;
+}
--- /dev/null
+/* 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 <openssl/err.h>
+#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 };
+
+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
--- /dev/null
+/* 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 <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#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];
+}
--- /dev/null
+#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
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#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);
+}
--- /dev/null
+#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