diff options
Diffstat (limited to 'src/lib-master/master-service-ssl-settings.c')
-rw-r--r-- | src/lib-master/master-service-ssl-settings.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/lib-master/master-service-ssl-settings.c b/src/lib-master/master-service-ssl-settings.c new file mode 100644 index 0000000..5ddf18c --- /dev/null +++ b/src/lib-master/master-service-ssl-settings.c @@ -0,0 +1,275 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "settings-parser.h" +#include "master-service-private.h" +#include "master-service-ssl-settings.h" +#include "iostream-ssl.h" + +#include <stddef.h> + +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type(#name, name, struct master_service_ssl_settings) + +static bool +master_service_ssl_settings_check(void *_set, pool_t pool, const char **error_r); + +static const struct setting_define master_service_ssl_setting_defines[] = { + DEF(ENUM, ssl), + DEF(STR, ssl_ca), + DEF(STR, ssl_client_ca_file), + DEF(STR, ssl_client_ca_dir), + DEF(STR, ssl_client_cert), + DEF(STR, ssl_client_key), + DEF(STR, ssl_cipher_list), + DEF(STR, ssl_cipher_suites), + DEF(STR, ssl_curve_list), + DEF(STR, ssl_min_protocol), + DEF(STR, ssl_cert_username_field), + DEF(STR, ssl_crypto_device), + DEF(BOOL, ssl_verify_client_cert), + DEF(BOOL, ssl_client_require_valid_cert), + DEF(BOOL, ssl_require_crl), + DEF(BOOL, verbose_ssl), + DEF(BOOL, ssl_prefer_server_ciphers), + DEF(STR, ssl_options), /* parsed as a string to set bools */ + + SETTING_DEFINE_LIST_END +}; + +static const struct master_service_ssl_settings master_service_ssl_default_settings = { +#ifdef HAVE_SSL + .ssl = "yes:no:required", +#else + .ssl = "no:yes:required", +#endif + .ssl_ca = "", + .ssl_client_ca_file = "", + .ssl_client_ca_dir = "", + .ssl_client_cert = "", + .ssl_client_key = "", + .ssl_cipher_list = "ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH", + .ssl_cipher_suites = "", /* Use TLS library provided value */ + .ssl_curve_list = "", + .ssl_min_protocol = "TLSv1.2", + .ssl_cert_username_field = "commonName", + .ssl_crypto_device = "", + .ssl_verify_client_cert = FALSE, + .ssl_client_require_valid_cert = TRUE, + .ssl_require_crl = TRUE, + .verbose_ssl = FALSE, + .ssl_prefer_server_ciphers = FALSE, + .ssl_options = "", +}; + +const struct setting_parser_info master_service_ssl_setting_parser_info = { + .module_name = "ssl", + .defines = master_service_ssl_setting_defines, + .defaults = &master_service_ssl_default_settings, + + .type_offset = SIZE_MAX, + .struct_size = sizeof(struct master_service_ssl_settings), + + .parent_offset = SIZE_MAX, + .check_func = master_service_ssl_settings_check +}; + +#undef DEF +#define DEF(type, name) \ + SETTING_DEFINE_STRUCT_##type(#name, name, struct master_service_ssl_server_settings) + +static const struct setting_define master_service_ssl_server_setting_defines[] = { + DEF(STR, ssl_cert), + DEF(STR, ssl_key), + DEF(STR, ssl_alt_cert), + DEF(STR, ssl_alt_key), + DEF(STR, ssl_key_password), + DEF(STR, ssl_dh), + + SETTING_DEFINE_LIST_END +}; + +static const struct master_service_ssl_server_settings master_service_ssl_server_default_settings = { + .ssl_cert = "", + .ssl_key = "", + .ssl_alt_cert = "", + .ssl_alt_key = "", + .ssl_key_password = "", + .ssl_dh = "", +}; + +static const struct setting_parser_info *master_service_ssl_server_setting_dependencies[] = { + &master_service_ssl_setting_parser_info, + NULL +}; + +const struct setting_parser_info master_service_ssl_server_setting_parser_info = { + .module_name = "ssl-server", + .defines = master_service_ssl_server_setting_defines, + .defaults = &master_service_ssl_server_default_settings, + + .type_offset = SIZE_MAX, + .struct_size = sizeof(struct master_service_ssl_server_settings), + + .parent_offset = SIZE_MAX, + .dependencies = master_service_ssl_server_setting_dependencies, +}; + +/* <settings checks> */ +static bool +master_service_ssl_settings_check(void *_set, pool_t pool ATTR_UNUSED, + const char **error_r) +{ + struct master_service_ssl_settings *set = _set; + + if (strcmp(set->ssl, "no") == 0) { + /* disabled */ + return TRUE; + } +#ifndef HAVE_SSL + *error_r = t_strdup_printf("SSL support not compiled in but ssl=%s", + set->ssl); + return FALSE; +#else + /* we get called from many different tools, possibly with -O parameter, + and few of those tools care about SSL settings. so don't check + ssl_cert/ssl_key/etc validity here except in doveconf, because it + usually is just an extra annoyance. */ +#ifdef CONFIG_BINARY + if (*set->ssl_cert == '\0') { + *error_r = "ssl enabled, but ssl_cert not set"; + return FALSE; + } + if (*set->ssl_key == '\0') { + *error_r = "ssl enabled, but ssl_key not set"; + return FALSE; + } +#endif + if (set->ssl_verify_client_cert && *set->ssl_ca == '\0') { + *error_r = "ssl_verify_client_cert set, but ssl_ca not"; + return FALSE; + } + + /* Now explode the ssl_options string into individual flags */ + /* First set them all to defaults */ + set->parsed_opts.compression = FALSE; + set->parsed_opts.tickets = TRUE; + + /* Then modify anything specified in the string */ + const char **opts = t_strsplit_spaces(set->ssl_options, ", "); + const char *opt; + while ((opt = *opts++) != NULL) { + if (strcasecmp(opt, "compression") == 0) { + set->parsed_opts.compression = TRUE; + } else if (strcasecmp(opt, "no_compression") == 0) { +#ifdef CONFIG_BINARY + i_warning("DEPRECATED: no_compression is default, " + "so it is redundant in ssl_options"); +#endif + } else if (strcasecmp(opt, "no_ticket") == 0) { + set->parsed_opts.tickets = FALSE; + } else { + *error_r = t_strdup_printf("ssl_options: unknown flag: '%s'", + opt); + return FALSE; + } + } + +#ifndef HAVE_SSL_CTX_SET1_CURVES_LIST + if (*set->ssl_curve_list != '\0') { + *error_r = "ssl_curve_list is set, but the linked openssl " + "version does not support it"; + return FALSE; + } +#endif + + return TRUE; +#endif +} +/* </settings checks> */ + +const struct master_service_ssl_settings * +master_service_ssl_settings_get(struct master_service *service) +{ + return master_service_ssl_settings_get_from_parser(service->set_parser); +} + +const struct master_service_ssl_settings * +master_service_ssl_settings_get_from_parser(struct setting_parser_context *set_parser) +{ + void **sets; + + sets = settings_parser_get_list(set_parser); + return sets[1]; +} + +const struct master_service_ssl_server_settings * +master_service_ssl_server_settings_get(struct master_service *service) +{ + void **sets; + + i_assert(service->want_ssl_server); + sets = settings_parser_get_list(service->set_parser); + return sets[2]; +} + +static void master_service_ssl_common_settings_to_iostream_set( + const struct master_service_ssl_settings *ssl_set, pool_t pool, + struct ssl_iostream_settings *set_r) +{ + i_zero(set_r); + set_r->min_protocol = p_strdup(pool, ssl_set->ssl_min_protocol); + set_r->cipher_list = p_strdup(pool, ssl_set->ssl_cipher_list); + /* leave NULL if empty - let library decide */ + set_r->ciphersuites = p_strdup_empty(pool, ssl_set->ssl_cipher_suites); + /* NOTE: It's a bit questionable whether ssl_ca should be used for + clients. But at least for now it's needed for login-proxy. */ + set_r->ca = p_strdup_empty(pool, ssl_set->ssl_ca); + + set_r->crypto_device = p_strdup(pool, ssl_set->ssl_crypto_device); + set_r->cert_username_field = p_strdup(pool, ssl_set->ssl_cert_username_field); + + set_r->verbose = ssl_set->verbose_ssl; + set_r->verbose_invalid_cert = ssl_set->verbose_ssl; + set_r->skip_crl_check = !ssl_set->ssl_require_crl; + set_r->prefer_server_ciphers = ssl_set->ssl_prefer_server_ciphers; + set_r->compression = ssl_set->parsed_opts.compression; + set_r->tickets = ssl_set->parsed_opts.tickets; + set_r->curve_list = p_strdup(pool, ssl_set->ssl_curve_list); +} + +void master_service_ssl_client_settings_to_iostream_set( + const struct master_service_ssl_settings *ssl_set, pool_t pool, + struct ssl_iostream_settings *set_r) +{ + master_service_ssl_common_settings_to_iostream_set(ssl_set, pool, set_r); + + set_r->ca_file = p_strdup_empty(pool, ssl_set->ssl_client_ca_file); + set_r->ca_dir = p_strdup_empty(pool, ssl_set->ssl_client_ca_dir); + set_r->cert.cert = p_strdup_empty(pool, ssl_set->ssl_client_cert); + set_r->cert.key = p_strdup_empty(pool, ssl_set->ssl_client_key); + set_r->verify_remote_cert = ssl_set->ssl_client_require_valid_cert; + set_r->allow_invalid_cert = !set_r->verify_remote_cert; +} + +void master_service_ssl_server_settings_to_iostream_set( + const struct master_service_ssl_settings *ssl_set, + const struct master_service_ssl_server_settings *ssl_server_set, + pool_t pool, struct ssl_iostream_settings *set_r) +{ + master_service_ssl_common_settings_to_iostream_set(ssl_set, pool, set_r); + + set_r->cert.cert = p_strdup(pool, ssl_server_set->ssl_cert); + set_r->cert.key = p_strdup(pool, ssl_server_set->ssl_key); + set_r->cert.key_password = p_strdup(pool, ssl_server_set->ssl_key_password); + if (ssl_server_set->ssl_alt_cert != NULL && + *ssl_server_set->ssl_alt_cert != '\0') { + set_r->alt_cert.cert = p_strdup(pool, ssl_server_set->ssl_alt_cert); + set_r->alt_cert.key = p_strdup(pool, ssl_server_set->ssl_alt_key); + set_r->alt_cert.key_password = p_strdup(pool, ssl_server_set->ssl_key_password); + } + set_r->dh = p_strdup(pool, ssl_server_set->ssl_dh); + set_r->verify_remote_cert = ssl_set->ssl_verify_client_cert; + set_r->allow_invalid_cert = !set_r->verify_remote_cert; +} |