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".
This commit is contained in:
Přemysl Eric Janouch 2015-04-11 17:08:42 +02:00
parent 7951dc4d85
commit efcb9d17e5
1 changed files with 76 additions and 39 deletions

115
zyklonb.c
View File

@ -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;