Diff

util/sslconfig.lua @ 12802:4a8740e01813

Merge 0.12->trunk
author Kim Alvefur <zash@zash.se>
date Mon, 12 Dec 2022 07:10:54 +0100
parent 12481:2ee27587fec7
child 12975:d10957394a3c
line wrap: on
line diff
--- a/util/sslconfig.lua	Mon Dec 12 20:40:23 2022 +0100
+++ b/util/sslconfig.lua	Mon Dec 12 07:10:54 2022 +0100
@@ -3,9 +3,12 @@
 local type = type;
 local pairs = pairs;
 local rawset = rawset;
+local rawget = rawget;
+local error = error;
 local t_concat = table.concat;
 local t_insert = table.insert;
 local setmetatable = setmetatable;
+local resolve_path = require"util.paths".resolve_relative_path;
 
 local _ENV = nil;
 -- luacheck: std none
@@ -34,7 +37,7 @@
 			options[value] = true;
 		end
 	end
-	config[field] = options;
+	rawset(config, field, options)
 end
 
 handlers.verifyext = handlers.options;
@@ -70,6 +73,20 @@
 -- TLS 1.3 ciphers
 finalisers.ciphersuites = finalisers.ciphers;
 
+-- Path expansion
+function finalisers.key(path, config)
+	if type(path) == "string" then
+		return resolve_path(config._basedir, path);
+	else
+		return nil
+	end
+end
+finalisers.certificate = finalisers.key;
+finalisers.cafile = finalisers.key;
+finalisers.capath = finalisers.key;
+-- XXX: copied from core/certmanager.lua, but this seems odd, because it would remove a dhparam function from the config
+finalisers.dhparam = finalisers.key;
+
 -- protocol = "x" should enable only that protocol
 -- protocol = "x+" should enable x and later versions
 
@@ -89,37 +106,81 @@
 
 -- Merge options from 'new' config into 'config'
 local function apply(config, new)
+	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
 end
 
 -- Finalize the config into the form LuaSec expects
 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);
 	return output;
 end
 
+local function build(config)
+	local cached = rawget(config, "_cache");
+	if cached then
+		return cached, nil
+	end
+
+	local ctx, err = rawget(config, "_context_factory")(config:final(), config);
+	if ctx then
+		rawset(config, "_cache", ctx);
+	end
+	return ctx, err
+end
+
 local sslopts_mt = {
 	__index = {
 		apply = apply;
 		final = final;
+		build = build;
 	};
+	__newindex = function()
+		error("SSL config objects cannot be modified directly. Use :apply()")
+	end;
 };
 
-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
+end
+
+sslopts_mt.__index.clone = clone;
+
 return {
 	apply = apply;
 	final = final;
-	new = new;
+	_new = new;
 };