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;