/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "module-dir.h" #include "iostream-ssl-private.h" #define OFFSET(name) offsetof(struct ssl_iostream_settings, name) static const size_t ssl_iostream_settings_string_offsets[] = { OFFSET(min_protocol), OFFSET(cipher_list), OFFSET(ciphersuites), OFFSET(curve_list), OFFSET(ca), OFFSET(ca_file), OFFSET(ca_dir), OFFSET(cert.cert), OFFSET(cert.key), OFFSET(cert.key_password), OFFSET(alt_cert.cert), OFFSET(alt_cert.key), OFFSET(alt_cert.key_password), OFFSET(dh), OFFSET(cert_username_field), OFFSET(crypto_device), }; static bool ssl_module_loaded = FALSE; #ifdef HAVE_SSL static struct module *ssl_module = NULL; #endif static const struct iostream_ssl_vfuncs *ssl_vfuncs = NULL; #ifdef HAVE_SSL static void ssl_module_unload(void) { ssl_iostream_context_cache_free(); module_dir_unload(&ssl_module); } #endif void iostream_ssl_module_init(const struct iostream_ssl_vfuncs *vfuncs) { ssl_vfuncs = vfuncs; ssl_module_loaded = TRUE; } int ssl_module_load(const char **error_r) { #ifdef HAVE_SSL const char *plugin_name = "ssl_iostream_openssl"; struct module_dir_load_settings mod_set; i_zero(&mod_set); mod_set.abi_version = DOVECOT_ABI_VERSION; mod_set.setting_name = ""; mod_set.require_init_funcs = TRUE; ssl_module = module_dir_load(MODULE_DIR, plugin_name, &mod_set); if (module_dir_try_load_missing(&ssl_module, MODULE_DIR, plugin_name, &mod_set, error_r) < 0) return -1; module_dir_init(ssl_module); if (!ssl_module_loaded) { *error_r = t_strdup_printf( "%s didn't call iostream_ssl_module_init() - SSL not initialized", plugin_name); module_dir_unload(&ssl_module); return -1; } /* Destroy SSL module after (most of) the others. Especially lib-fs backends may still want to access SSL module in their own atexit-callbacks. */ lib_atexit_priority(ssl_module_unload, LIB_ATEXIT_PRIORITY_LOW); return 0; #else *error_r = "SSL support not compiled in"; return -1; #endif } int io_stream_ssl_global_init(const struct ssl_iostream_settings *set, const char **error_r) { return ssl_vfuncs->global_init(set, error_r); } int ssl_iostream_context_init_client(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r) { struct ssl_iostream_settings set_copy = *set; /* ensure this is set to TRUE */ set_copy.verify_remote_cert = TRUE; if (!ssl_module_loaded) { if (ssl_module_load(error_r) < 0) return -1; } if (io_stream_ssl_global_init(&set_copy, error_r) < 0) return -1; return ssl_vfuncs->context_init_client(&set_copy, ctx_r, error_r); } int ssl_iostream_context_init_server(const struct ssl_iostream_settings *set, struct ssl_iostream_context **ctx_r, const char **error_r) { if (!ssl_module_loaded) { if (ssl_module_load(error_r) < 0) return -1; } if (io_stream_ssl_global_init(set, error_r) < 0) return -1; return ssl_vfuncs->context_init_server(set, ctx_r, error_r); } void ssl_iostream_context_ref(struct ssl_iostream_context *ctx) { ssl_vfuncs->context_ref(ctx); } void ssl_iostream_context_unref(struct ssl_iostream_context **_ctx) { struct ssl_iostream_context *ctx = *_ctx; if (*_ctx == NULL) return; *_ctx = NULL; ssl_vfuncs->context_unref(ctx); } int io_stream_create_ssl_client(struct ssl_iostream_context *ctx, const char *host, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r) { struct ssl_iostream_settings set_copy = *set; set_copy.verify_remote_cert = TRUE; return ssl_vfuncs->create(ctx, host, &set_copy, input, output, iostream_r, error_r); } int io_stream_create_ssl_server(struct ssl_iostream_context *ctx, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r, const char **error_r) { return ssl_vfuncs->create(ctx, NULL, set, input, output, iostream_r, error_r); } void ssl_iostream_unref(struct ssl_iostream **_ssl_io) { struct ssl_iostream *ssl_io = *_ssl_io; *_ssl_io = NULL; ssl_vfuncs->unref(ssl_io); } void ssl_iostream_destroy(struct ssl_iostream **_ssl_io) { struct ssl_iostream *ssl_io; if (_ssl_io == NULL || *_ssl_io == NULL) return; ssl_io = *_ssl_io; *_ssl_io = NULL; ssl_vfuncs->destroy(ssl_io); } void ssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io, const char *prefix) { ssl_vfuncs->set_log_prefix(ssl_io, prefix); } int ssl_iostream_handshake(struct ssl_iostream *ssl_io) { return ssl_vfuncs->handshake(ssl_io); } void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io, ssl_iostream_handshake_callback_t *callback, void *context) { ssl_vfuncs->set_handshake_callback(ssl_io, callback, context); } void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io, ssl_iostream_sni_callback_t *callback, void *context) { ssl_vfuncs->set_sni_callback(ssl_io, callback, context); } void ssl_iostream_change_context(struct ssl_iostream *ssl_io, struct ssl_iostream_context *ctx) { ssl_vfuncs->change_context(ssl_io, ctx); } bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->is_handshaked(ssl_io); } bool ssl_iostream_has_handshake_failed(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->has_handshake_failed(ssl_io); } bool ssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io) { return ssl_vfuncs->has_valid_client_cert(ssl_io); } bool ssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io) { return ssl_vfuncs->has_broken_client_cert(ssl_io); } bool ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, const char *name, const char **reason_r) { return ssl_vfuncs->cert_match_name(ssl_io, name, reason_r); } int ssl_iostream_check_cert_validity(struct ssl_iostream *ssl_io, const char *host, const char **error_r) { const char *reason; if (!ssl_iostream_has_valid_client_cert(ssl_io)) { if (!ssl_iostream_has_broken_client_cert(ssl_io)) *error_r = "SSL certificate not received"; else { *error_r = t_strdup(ssl_iostream_get_last_error(ssl_io)); if (*error_r == NULL) *error_r = "Received invalid SSL certificate"; } return -1; } else if (!ssl_iostream_cert_match_name(ssl_io, host, &reason)) { *error_r = t_strdup_printf( "SSL certificate doesn't match expected host name %s: %s", host, reason); return -1; } return 0; } const char *ssl_iostream_get_peer_name(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_peer_name(ssl_io); } const char *ssl_iostream_get_server_name(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_server_name(ssl_io); } const char *ssl_iostream_get_compression(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_compression(ssl_io); } const char *ssl_iostream_get_security_string(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_security_string(ssl_io); } const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_last_error(ssl_io); } struct ssl_iostream_settings *ssl_iostream_settings_dup(pool_t pool, const struct ssl_iostream_settings *old_set) { struct ssl_iostream_settings *new_set; new_set = p_new(pool, struct ssl_iostream_settings, 1); ssl_iostream_settings_init_from(pool, new_set, old_set); return new_set; } void ssl_iostream_settings_init_from(pool_t pool, struct ssl_iostream_settings *dest, const struct ssl_iostream_settings *src) { unsigned int i; *dest = *src; for (i = 0; i < N_ELEMENTS(ssl_iostream_settings_string_offsets); i++) { const size_t offset = ssl_iostream_settings_string_offsets[i]; const char *const *src_str = CONST_PTR_OFFSET(src, offset); const char **dest_str = PTR_OFFSET(dest, offset); *dest_str = p_strdup(pool, *src_str); } } bool ssl_iostream_settings_equals(const struct ssl_iostream_settings *set1, const struct ssl_iostream_settings *set2) { struct ssl_iostream_settings set1_nonstr, set2_nonstr; unsigned int i; set1_nonstr = *set1; set2_nonstr = *set2; for (i = 0; i < N_ELEMENTS(ssl_iostream_settings_string_offsets); i++) { const size_t offset = ssl_iostream_settings_string_offsets[i]; const char **str1 = PTR_OFFSET(&set1_nonstr, offset); const char **str2 = PTR_OFFSET(&set2_nonstr, offset); if (null_strcmp(*str1, *str2) != 0) return FALSE; /* clear away the string pointer from the settings struct */ *str1 = NULL; *str2 = NULL; } /* The set*_nonstr no longer have any pointers, so we can compare them directly. */ return memcmp(&set1_nonstr, &set2_nonstr, sizeof(set1_nonstr)) == 0; } void ssl_iostream_settings_drop_stream_only(struct ssl_iostream_settings *set) { set->verbose = FALSE; set->verbose_invalid_cert = FALSE; set->allow_invalid_cert = FALSE; } const char *ssl_iostream_get_cipher(struct ssl_iostream *ssl_io, unsigned int *bits_r) { return ssl_vfuncs->get_cipher(ssl_io, bits_r); } const char *ssl_iostream_get_pfs(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_pfs(ssl_io); } const char *ssl_iostream_get_protocol_name(struct ssl_iostream *ssl_io) { return ssl_vfuncs->get_protocol_name(ssl_io); }