From efcb9d17e5c784a58a11117a960ee25022183619 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Sat, 11 Apr 2015 17:08:42 +0200
Subject: ZyklonB: better SSL configuration
Allow specifying a custom certificate bundle (can be handy for
connecting to servers with a self-signed certificate).
Enabled certificate verification by default.
Renamed the "ssl_use" to just "ssl".
---
zyklonb.c | 115 +++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 76 insertions(+), 39 deletions(-)
diff --git a/zyklonb.c b/zyklonb.c
index d375b0c..3a5dd44 100644
--- a/zyklonb.c
+++ b/zyklonb.c
@@ -33,8 +33,11 @@ static struct config_item g_config_table[] =
{ "irc_host", NULL, "Address of the IRC server" },
{ "irc_port", "6667", "Port of the IRC server" },
- { "ssl_use", "off", "Whether to use SSL" },
+ { "ssl", "off", "Whether to use SSL" },
{ "ssl_cert", NULL, "Client SSL certificate (PEM)" },
+ { "ssl_verify", "on", "Whether to verify certificates" },
+ { "ssl_ca_file", NULL, "OpenSSL CA bundle file" },
+ { "ssl_ca_path", NULL, "OpenSSL CA bundle path" },
{ "autojoin", NULL, "Channels to join on start" },
{ "reconnect", "on", "Whether to reconnect on error" },
{ "reconnect_delay", "5", "Time between reconnecting" },
@@ -294,16 +297,67 @@ irc_send (struct bot_context *ctx, const char *format, ...)
return result;
}
-static int
-irc_ssl_verify_callback (int preverify_ok, X509_STORE_CTX *x509_ctx)
+static bool
+irc_get_boolean_from_config
+ (struct bot_context *ctx, const char *name, bool *value, struct error **e)
{
- (void) x509_ctx;
+ const char *str = str_map_find (&ctx->config, name);
+ hard_assert (str != NULL);
- if (!preverify_ok)
- print_warning ("TLS certificate verification failed");
+ if (set_boolean_if_valid (value, str))
+ return true;
- // We don't care; some encryption is always better than no encryption
- return 1;
+ error_set (e, "invalid configuration value for `%s'", name);
+ return false;
+}
+
+static bool
+irc_initialize_ssl_ctx (struct bot_context *ctx, struct error **e)
+{
+ // XXX: maybe we should call SSL_CTX_set_options() for some workarounds
+
+ bool verify;
+ if (!irc_get_boolean_from_config (ctx, "ssl_verify", &verify, e))
+ return false;
+
+ if (!verify)
+ SSL_CTX_set_verify (ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+ const char *ca_file = str_map_find (&ctx->config, "ca_file");
+ const char *ca_path = str_map_find (&ctx->config, "ca_path");
+
+ struct error *error = NULL;
+ if (ca_file || ca_path)
+ {
+ if (SSL_CTX_load_verify_locations (ctx->ssl_ctx, ca_file, ca_path))
+ return true;
+
+ error_set (&error, "%s: %s",
+ "failed to set locations for the CA certificate bundle",
+ ERR_reason_error_string (ERR_get_error ()));
+ goto ca_error;
+ }
+
+ if (!SSL_CTX_set_default_verify_paths (ctx->ssl_ctx))
+ {
+ error_set (&error, "%s: %s",
+ "couldn't load the default CA certificate bundle",
+ ERR_reason_error_string (ERR_get_error ()));
+ goto ca_error;
+ }
+ return true;
+
+ca_error:
+ if (verify)
+ {
+ error_propagate (e, error);
+ return false;
+ }
+
+ // Only inform the user if we're not actually verifying
+ print_warning ("%s", error->message);
+ error_free (error);
+ return true;
}
static bool
@@ -313,10 +367,8 @@ irc_initialize_ssl (struct bot_context *ctx, struct error **e)
ctx->ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
if (!ctx->ssl_ctx)
goto error_ssl_1;
- if (!SSL_CTX_set_default_verify_paths (ctx->ssl_ctx))
- print_warning ("couldn't load TLS CA certificates");
- SSL_CTX_set_verify (ctx->ssl_ctx, SSL_VERIFY_PEER, irc_ssl_verify_callback);
- // XXX: maybe we should call SSL_CTX_set_options() for some workarounds
+ if (!irc_initialize_ssl_ctx (ctx, e))
+ goto error_ssl_2;
ctx->ssl = SSL_new (ctx->ssl_ctx);
if (!ctx->ssl)
@@ -630,20 +682,14 @@ prepare_recovery_environment (void)
}
}
-static void
-setup_recovery_handler (struct bot_context *ctx)
+static bool
+setup_recovery_handler (struct bot_context *ctx, struct error **e)
{
- const char *recover_str = str_map_find (&ctx->config, "recover");
- hard_assert (recover_str != NULL); // We have a default value for this
-
bool recover;
- if (!set_boolean_if_valid (&recover, recover_str))
- {
- print_error ("invalid configuration value for `%s'", "recover");
- exit (EXIT_FAILURE);
- }
+ if (!irc_get_boolean_from_config (ctx, "recover", &recover, e))
+ return false;
if (!recover)
- return;
+ return true;
// Make sure these signals aren't blocked, otherwise we would be unable
// to handle them, making the critical conditions fatal.
@@ -668,6 +714,7 @@ setup_recovery_handler (struct bot_context *ctx)
|| sigaction (SIGFPE, &sa, NULL) == -1
|| sigaction (SIGILL, &sa, NULL) == -1)
print_error ("sigaction: %s", strerror (errno));
+ return true;
}
// --- SOCKS 5/4a (blocking implementation) ------------------------------------
@@ -2000,7 +2047,6 @@ irc_connect (struct bot_context *ctx, struct error **e)
{
const char *irc_host = str_map_find (&ctx->config, "irc_host");
const char *irc_port = str_map_find (&ctx->config, "irc_port");
- const char *ssl_use_str = str_map_find (&ctx->config, "ssl_use");
const char *socks_host = str_map_find (&ctx->config, "socks_host");
const char *socks_port = str_map_find (&ctx->config, "socks_port");
@@ -2012,7 +2058,7 @@ irc_connect (struct bot_context *ctx, struct error **e)
const char *realname = str_map_find (&ctx->config, "realname");
// We have a default value for these
- hard_assert (irc_port && ssl_use_str && socks_port);
+ hard_assert (irc_port && socks_port);
hard_assert (nickname && username && realname);
// TODO: again, get rid of `struct error' in here. The question is: how
@@ -2024,11 +2070,8 @@ irc_connect (struct bot_context *ctx, struct error **e)
}
bool use_ssl;
- if (!set_boolean_if_valid (&use_ssl, ssl_use_str))
- {
- error_set (e, "invalid configuration value for `%s'", "use_ssl");
+ if (!irc_get_boolean_from_config (ctx, "ssl", &use_ssl, e))
return false;
- }
if (socks_host)
{
@@ -2078,13 +2121,8 @@ irc_connect (struct bot_context *ctx, struct error **e)
static bool
parse_config (struct bot_context *ctx, struct error **e)
{
- const char *reconnect_str = str_map_find (&ctx->config, "reconnect");
- hard_assert (reconnect_str != NULL); // We have a default value for this
- if (!set_boolean_if_valid (&ctx->reconnect, reconnect_str))
- {
- error_set (e, "invalid configuration value for `%s'", "reconnect");
+ if (!irc_get_boolean_from_config (ctx, "reconnect", &ctx->reconnect, e))
return false;
- }
const char *delay_str = str_map_find (&ctx->config, "reconnect_delay");
hard_assert (delay_str != NULL); // We have a default value for this
@@ -2248,15 +2286,14 @@ main (int argc, char *argv[])
bot_context_init (&ctx);
struct error *e = NULL;
- if (!read_config_file (&ctx.config, &e))
+ if (!read_config_file (&ctx.config, &e)
+ || !setup_recovery_handler (&ctx, &e))
{
- print_error ("error loading configuration: %s", e->message);
+ print_error ("%s", e->message);
error_free (e);
exit (EXIT_FAILURE);
}
- setup_recovery_handler (&ctx);
-
poller_fd_init (&ctx.signal_event, &ctx.poller, g_signal_pipe[0]);
ctx.signal_event.dispatcher = (poller_fd_fn) on_signal_pipe_readable;
ctx.signal_event.user_data = &ctx;
--
cgit v1.2.3-70-g09d2