comparison util/sslconfig.lua @ 12481:2ee27587fec7

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.
author Jonas Schäfer <jonas@wielicki.name>
date Sat, 02 Apr 2022 11:15:33 +0200
parents 7e9ebdc75ce4
children d10957394a3c
comparison
equal deleted inserted replaced
12480:7e9ebdc75ce4 12481:2ee27587fec7
6 local rawget = rawget; 6 local rawget = rawget;
7 local error = error; 7 local error = error;
8 local t_concat = table.concat; 8 local t_concat = table.concat;
9 local t_insert = table.insert; 9 local t_insert = table.insert;
10 local setmetatable = setmetatable; 10 local setmetatable = setmetatable;
11 local config_path = prosody.paths.config or ".";
12 local resolve_path = require"util.paths".resolve_relative_path; 11 local resolve_path = require"util.paths".resolve_relative_path;
13
14 -- TODO: use net.server directly here
15 local tls_impl = require"net.tls_luasec";
16 12
17 local _ENV = nil; 13 local _ENV = nil;
18 -- luacheck: std none 14 -- luacheck: std none
19 15
20 local handlers = { }; 16 local handlers = { };
76 72
77 -- TLS 1.3 ciphers 73 -- TLS 1.3 ciphers
78 finalisers.ciphersuites = finalisers.ciphers; 74 finalisers.ciphersuites = finalisers.ciphers;
79 75
80 -- Path expansion 76 -- Path expansion
81 function finalisers.key(path) 77 function finalisers.key(path, config)
82 if type(path) == "string" then 78 if type(path) == "string" then
83 return resolve_path(config_path, path); 79 return resolve_path(config._basedir, path);
84 else 80 else
85 return nil 81 return nil
86 end 82 end
87 end 83 end
88 finalisers.certificate = finalisers.key; 84 finalisers.certificate = finalisers.key;
108 end 104 end
109 end 105 end
110 106
111 -- Merge options from 'new' config into 'config' 107 -- Merge options from 'new' config into 'config'
112 local function apply(config, new) 108 local function apply(config, new)
113 -- 0 == cache 109 rawset(config, "_cache", nil);
114 rawset(config, 0, nil);
115 if type(new) == "table" then 110 if type(new) == "table" then
116 for field, value in pairs(new) do 111 for field, value in pairs(new) do
117 (handlers[field] or rawset)(config, field, value); 112 -- exclude keys which are internal to the config builder
113 if field:sub(1, 1) ~= "_" then
114 (handlers[field] or rawset)(config, field, value);
115 end
118 end 116 end
119 end 117 end
120 return config 118 return config
121 end 119 end
122 120
123 -- Finalize the config into the form LuaSec expects 121 -- Finalize the config into the form LuaSec expects
124 local function final(config) 122 local function final(config)
125 local output = { }; 123 local output = { };
126 for field, value in pairs(config) do 124 for field, value in pairs(config) do
127 output[field] = (finalisers[field] or id)(value); 125 -- exclude keys which are internal to the config builder
126 if field:sub(1, 1) ~= "_" then
127 output[field] = (finalisers[field] or id)(value, config);
128 end
128 end 129 end
129 -- Need to handle protocols last because it adds to the options list 130 -- Need to handle protocols last because it adds to the options list
130 protocol(output); 131 protocol(output);
131 return output; 132 return output;
132 end 133 end
133 134
134 local function build(config) 135 local function build(config)
135 local cached = rawget(config, 0); 136 local cached = rawget(config, "_cache");
136 if cached then 137 if cached then
137 return cached, nil 138 return cached, nil
138 end 139 end
139 140
140 local ctx, err = tls_impl.new_context(config:final(), config); 141 local ctx, err = rawget(config, "_context_factory")(config:final(), config);
141 if ctx then 142 if ctx then
142 rawset(config, 0, ctx); 143 rawset(config, "_cache", ctx);
143 end 144 end
144 return ctx, err 145 return ctx, err
145 end 146 end
146 147
147 local sslopts_mt = { 148 local sslopts_mt = {
154 error("SSL config objects cannot be modified directly. Use :apply()") 155 error("SSL config objects cannot be modified directly. Use :apply()")
155 end; 156 end;
156 }; 157 };
157 158
158 159
159 local function new() 160 -- passing basedir through everything is required to avoid sslconfig depending
160 return setmetatable({options={}}, sslopts_mt); 161 -- on prosody.paths.config
162 local function new(context_factory, basedir)
163 return setmetatable({
164 _context_factory = context_factory,
165 _basedir = basedir,
166 options={},
167 }, sslopts_mt);
161 end 168 end
162 169
163 local function clone(config) 170 local function clone(config)
164 local result = new(); 171 local result = new();
165 for k, v in pairs(config) do 172 for k, v in pairs(config) do
173 -- note that we *do* copy the internal keys on clone -- we have to carry
174 -- both the factory and the cache with us
166 rawset(result, k, v); 175 rawset(result, k, v);
167 end 176 end
168 return result 177 return result
169 end 178 end
170 179
171 sslopts_mt.__index.clone = clone; 180 sslopts_mt.__index.clone = clone;
172 181
173 return { 182 return {
174 apply = apply; 183 apply = apply;
175 final = final; 184 final = final;
176 new = new; 185 _new = new;
177 }; 186 };