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:
		
							
								
								
									
										115
									
								
								zyklonb.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								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;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user