summaryrefslogtreecommitdiffstats
path: root/src/lib-ssl-iostream/iostream-ssl.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-ssl-iostream/iostream-ssl.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/lib-ssl-iostream/iostream-ssl.c b/src/lib-ssl-iostream/iostream-ssl.c
new file mode 100644
index 0000000..430e69d
--- /dev/null
+++ b/src/lib-ssl-iostream/iostream-ssl.c
@@ -0,0 +1,351 @@
+/* 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 = "<built-in lib-ssl-iostream lookup>";
+ 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);
+}