net: refactor sslconfig to not depend on LuaSec
authorJonas Schäfer <jonas@wielicki.name>
Sat, 02 Apr 2022 11:15:33 +0200
changeset 12485 2ee27587fec7
parent 12484 7e9ebdc75ce4
child 12486 b193f8a2737e
net: refactor sslconfig to not depend on LuaSec This now requires that the network backend exposes a tls_builder function, which essentially wraps the former util.sslconfig.new() function, passing a factory to create the eventual SSL context. That allows a net.server backend to pick whatever it likes as SSL context factory, as long as it understands the config table passed by the SSL config builder. Heck, a backend could even mock and replace the entire SSL config builder API.
core/certmanager.lua
net/server.lua
net/server_epoll.lua
net/server_event.lua
net/server_select.lua
util/sslconfig.lua
--- a/core/certmanager.lua	Wed Apr 27 17:44:14 2022 +0200
+++ b/core/certmanager.lua	Sat Apr 02 11:15:33 2022 +0200
@@ -10,7 +10,7 @@
 local configmanager = require "core.configmanager";
 local log = require "util.logger".init("certmanager");
 local ssl_newcontext = ssl.newcontext;
-local new_config = require"util.sslconfig".new;
+local new_config = require"net.server".tls_builder;
 local stat = require "lfs".attributes;
 
 local x509 = require "util.x509";
--- a/net/server.lua	Wed Apr 27 17:44:14 2022 +0200
+++ b/net/server.lua	Sat Apr 02 11:15:33 2022 +0200
@@ -118,6 +118,13 @@
 	prosody.events.add_handler("config-reloaded", load_config);
 end
 
+local tls_builder = server.tls_builder;
+-- resolving the basedir here avoids util.sslconfig depending on
+-- prosody.paths.config
+function server.tls_builder()
+	return tls_builder(prosody.paths.config or "")
+end
+
 -- require "net.server" shall now forever return this,
 -- ie. server_select or server_event as chosen above.
 return server;
--- a/net/server_epoll.lua	Wed Apr 27 17:44:14 2022 +0200
+++ b/net/server_epoll.lua	Sat Apr 02 11:15:33 2022 +0200
@@ -27,6 +27,8 @@
 local _SOCKETINVALID = socket._SOCKETINVALID or -1;
 local new_id = require "util.id".short;
 local xpcall = require "util.xpcall".xpcall;
+local sslconfig = require "util.sslconfig";
+local tls_impl = require "net.tls_luasec";
 
 local poller = require "util.poll"
 local EEXIST = poller.EEXIST;
@@ -1104,6 +1106,10 @@
 		cfg = setmetatable(newconfig, default_config);
 	end;
 
+	tls_builder = function(basedir)
+		return sslconfig._new(tls_impl.new_context, basedir)
+	end,
+
 	-- libevent emulation
 	event = { EV_READ = "r", EV_WRITE = "w", EV_READWRITE = "rw", EV_LEAVE = -1 };
 	addevent = function (fd, mode, callback)
--- a/net/server_event.lua	Wed Apr 27 17:44:14 2022 +0200
+++ b/net/server_event.lua	Sat Apr 02 11:15:33 2022 +0200
@@ -52,6 +52,8 @@
 local levent = require "luaevent.core"
 local inet = require "util.net";
 local inet_pton = inet.pton;
+local sslconfig = require "util.sslconfig";
+local tls_impl = require "net.tls_luasec";
 
 local socket_gettime = socket.gettime
 
@@ -944,6 +946,10 @@
 	add_task = add_task,
 	watchfd = watchfd,
 
+	tls_builder = function(basedir)
+		return sslconfig._new(tls_impl.new_context, basedir)
+	end,
+
 	__NAME = SCRIPT_NAME,
 	__DATE = LAST_MODIFIED,
 	__AUTHOR = SCRIPT_AUTHOR,
--- a/net/server_select.lua	Wed Apr 27 17:44:14 2022 +0200
+++ b/net/server_select.lua	Sat Apr 02 11:15:33 2022 +0200
@@ -52,6 +52,8 @@
 local luasocket_gettime = luasocket.gettime
 local inet = require "util.net";
 local inet_pton = inet.pton;
+local sslconfig = require "util.sslconfig";
+local tls_impl = require "net.tls_luasec";
 
 --// extern lib methods //--
 
@@ -1181,4 +1183,8 @@
 	removeserver = removeserver,
 	get_backend = get_backend,
 	changesettings = changesettings,
+
+	tls_builder = function(basedir)
+		return sslconfig._new(tls_impl.new_context, basedir)
+	end,
 }
--- a/util/sslconfig.lua	Wed Apr 27 17:44:14 2022 +0200
+++ b/util/sslconfig.lua	Sat Apr 02 11:15:33 2022 +0200
@@ -8,12 +8,8 @@
 local t_concat = table.concat;
 local t_insert = table.insert;
 local setmetatable = setmetatable;
-local config_path = prosody.paths.config or ".";
 local resolve_path = require"util.paths".resolve_relative_path;
 
--- TODO: use net.server directly here
-local tls_impl  = require"net.tls_luasec";
-
 local _ENV = nil;
 -- luacheck: std none
 
@@ -78,9 +74,9 @@
 finalisers.ciphersuites = finalisers.ciphers;
 
 -- Path expansion
-function finalisers.key(path)
+function finalisers.key(path, config)
 	if type(path) == "string" then
-		return resolve_path(config_path, path);
+		return resolve_path(config._basedir, path);
 	else
 		return nil
 	end
@@ -110,11 +106,13 @@
 
 -- Merge options from 'new' config into 'config'
 local function apply(config, new)
-	-- 0 == cache
-	rawset(config, 0, nil);
+	rawset(config, "_cache", nil);
 	if type(new) == "table" then
 		for field, value in pairs(new) do
-			(handlers[field] or rawset)(config, field, value);
+			-- exclude keys which are internal to the config builder
+			if field:sub(1, 1) ~= "_" then
+				(handlers[field] or rawset)(config, field, value);
+			end
 		end
 	end
 	return config
@@ -124,7 +122,10 @@
 local function final(config)
 	local output = { };
 	for field, value in pairs(config) do
-		output[field] = (finalisers[field] or id)(value);
+		-- exclude keys which are internal to the config builder
+		if field:sub(1, 1) ~= "_" then
+			output[field] = (finalisers[field] or id)(value, config);
+		end
 	end
 	-- Need to handle protocols last because it adds to the options list
 	protocol(output);
@@ -132,14 +133,14 @@
 end
 
 local function build(config)
-	local cached = rawget(config, 0);
+	local cached = rawget(config, "_cache");
 	if cached then
 		return cached, nil
 	end
 
-	local ctx, err = tls_impl.new_context(config:final(), config);
+	local ctx, err = rawget(config, "_context_factory")(config:final(), config);
 	if ctx then
-		rawset(config, 0, ctx);
+		rawset(config, "_cache", ctx);
 	end
 	return ctx, err
 end
@@ -156,13 +157,21 @@
 };
 
 
-local function new()
-	return setmetatable({options={}}, sslopts_mt);
+-- passing basedir through everything is required to avoid sslconfig depending
+-- on prosody.paths.config
+local function new(context_factory, basedir)
+	return setmetatable({
+		_context_factory = context_factory,
+		_basedir = basedir,
+		options={},
+	}, sslopts_mt);
 end
 
 local function clone(config)
 	local result = new();
 	for k, v in pairs(config) do
+		-- note that we *do* copy the internal keys on clone -- we have to carry
+		-- both the factory and the cache with us
 		rawset(result, k, v);
 	end
 	return result
@@ -173,5 +182,5 @@
 return {
 	apply = apply;
 	final = final;
-	new = new;
+	_new = new;
 };