diff options
-rw-r--r-- | core/certmanager.lua | 14 | ||||
-rw-r--r-- | core/moduleapi.lua | 1 | ||||
-rw-r--r-- | net/http.lua | 3 | ||||
-rw-r--r-- | net/server_event.lua | 17 | ||||
-rw-r--r-- | net/tls_luasec.lua | 5 | ||||
-rw-r--r-- | plugins/mod_auth_internal_hashed.lua | 3 | ||||
-rw-r--r-- | plugins/mod_auth_internal_plain.lua | 3 | ||||
-rw-r--r-- | plugins/mod_c2s.lua | 18 | ||||
-rw-r--r-- | plugins/mod_csi.lua | 21 | ||||
-rw-r--r-- | plugins/mod_s2s.lua | 27 | ||||
-rw-r--r-- | plugins/mod_s2s_bidi.lua | 7 | ||||
-rw-r--r-- | spec/util_xtemplate_spec.lua | 4 | ||||
-rw-r--r-- | teal-src/prosody/util/xtemplate.tl | 5 | ||||
-rw-r--r-- | util/sslconfig.lua | 14 | ||||
-rw-r--r-- | util/xtemplate.lua | 3 |
15 files changed, 130 insertions, 15 deletions
diff --git a/core/certmanager.lua b/core/certmanager.lua index 263797e5..9e0ace6a 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -213,6 +213,18 @@ local core_defaults = { dane = tls.features.capabilities.dane and configmanager.get("*", "use_dane") and { "no_ee_namechecks" }; } +-- https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.1 +local ffdhe2048 = [[ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS----- +]] + local mozilla_ssl_configs = { -- https://wiki.mozilla.org/Security/Server_Side_TLS -- Version 5.7 as of 2023-07-09 @@ -225,7 +237,7 @@ local mozilla_ssl_configs = { }; intermediate = { protocol = "tlsv1_2+"; - dhparam = nil; -- ffdhe2048.txt + dhparam = ffdhe2048; options = { cipher_server_preference = false }; ciphers = { "ECDHE-ECDSA-AES128-GCM-SHA256"; diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 31d1b1bd..fa5086cf 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -653,6 +653,7 @@ end function api:metric(type_, name, unit, description, label_keys, conf) local metric = require "prosody.core.statsmanager".metric; local is_scoped = self.host ~= "*" + label_keys = label_keys or {}; if is_scoped then -- prepend `host` label to label keys if this is not a global module local orig_labels = label_keys diff --git a/net/http.lua b/net/http.lua index bea1e905..35a92d57 100644 --- a/net/http.lua +++ b/net/http.lua @@ -317,6 +317,9 @@ local function request(self, u, ex, callback) if ex and ex.use_dane ~= nil then use_dane = ex.use_dane; end + if not sslctx then + error("Attempt to make HTTPS request but no 'sslctx' provided in options"); + end end if self.pool then diff --git a/net/server_event.lua b/net/server_event.lua index 3bad3474..44222aa3 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -735,7 +735,10 @@ local function handleserver( server, addr, port, pattern, listener, sslctx, star debug( "maximal connections reached, refuse client connection; accept delay:", delay ) return EV_TIMEOUT, delay -- delay for next accept attempt end - local client_ip, client_port = client:getpeername( ) + local client_ip, client_port = addr, port; + if client.getpeername then -- Only IP sockets have this method, UNIX sockets don't + client_ip, client_port = client:getpeername( ) + end interface._connections = interface._connections + 1 -- increase connection count local clientinterface = handleclient( client, client_ip, client_port, interface, pattern, listener, sslctx ) --vdebug( "client id:", clientinterface, "startssl:", startssl ) @@ -758,6 +761,17 @@ local function handleserver( server, addr, port, pattern, listener, sslctx, star return interface end +local function wrapserver(conn, addr, port, listeners, config) + config = config or {} + if config.sslctx and not has_luasec then + debug "fatal error: luasec not found" + return nil, "luasec not found" + end + local interface = handleserver( conn, addr, port, config.read_size, listeners, config.tls_ctx, config.tls_direct) -- new server handler + debug( "new server created with id:", tostring(interface)) + return interface +end + local function listen(addr, port, listener, config) config = config or {} if config.sslctx and not has_luasec then @@ -947,6 +961,7 @@ return { listen = listen, addclient = addclient, wrapclient = wrapclient, + wrapserver = wrapserver, setquitting = setquitting, closeall = closeallservers, get_backend = get_backend, diff --git a/net/tls_luasec.lua b/net/tls_luasec.lua index 3af2fc6b..4e4e92ed 100644 --- a/net/tls_luasec.lua +++ b/net/tls_luasec.lua @@ -54,7 +54,10 @@ local function new_context(cfg, builder) -- LuaSec expects dhparam to be a callback that takes two arguments. -- We ignore those because it is mostly used for having a separate -- set of params for EXPORT ciphers, which we don't have by default. - if type(cfg.dhparam) == "string" then + if type(cfg.dhparam) == "string" and cfg.dhparam:sub(1, 10) == "-----BEGIN" then + local dhparam = cfg.dhparam; + cfg.dhparam = function() return dhparam; end + elseif type(cfg.dhparam) == "string" then local f, err = io_open(cfg.dhparam); if not f then return nil, "Could not open DH parameters: "..err end local dhparam = f:read("*a"); diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 4840f431..806eb9bd 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -37,6 +37,9 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + if credentials.disabled then + return nil, "Account disabled."; + end password = saslprep(password); if not password then return nil, "Password fails SASLprep."; diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index 98df1983..6cced803 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -22,6 +22,9 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + if credentials.disabled then + return nil, "Account disabled."; + end password = saslprep(password); if not password then return nil, "Password fails SASLprep."; diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 1a24c27c..09d4be08 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -30,6 +30,12 @@ local stream_close_timeout = module:get_option_period("c2s_close_timeout", 5); local opt_keepalives = module:get_option_boolean("c2s_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true)); local stanza_size_limit = module:get_option_integer("c2s_stanza_size_limit", 1024*256,10000); +local advertised_idle_timeout = 14*60; -- default in all net.server implementations +local network_settings = module:get_option("network_settings"); +if type(network_settings) == "table" and type(network_settings.read_timeout) == "number" then + advertised_idle_timeout = network_settings.read_timeout; +end + local measure_connections = module:metric("gauge", "connections", "", "Established c2s connections", {"host", "type", "ip_family"}); local sessions = module:shared("sessions"); @@ -130,10 +136,16 @@ function stream_callbacks._streamopened(session, attr) local features = st.stanza("stream:features"); hosts[session.host].events.fire_event("stream-features", { origin = session, features = features, stream = attr }); if features.tags[1] or session.full_jid then - if stanza_size_limit then + if stanza_size_limit or advertised_idle_timeout then features:reset(); - features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }) - :text_tag("max-bytes", string.format("%d", stanza_size_limit)):up(); + local limits = features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }); + if stanza_size_limit then + limits:text_tag("max-bytes", string.format("%d", stanza_size_limit)); + end + if advertised_idle_timeout then + limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout)); + end + limits:reset(); end send(features); else diff --git a/plugins/mod_csi.lua b/plugins/mod_csi.lua index 82efd831..7a1857c0 100644 --- a/plugins/mod_csi.lua +++ b/plugins/mod_csi.lua @@ -3,6 +3,7 @@ local xmlns_csi = "urn:xmpp:csi:0"; local csi_feature = st.stanza("csi", { xmlns = xmlns_csi }); local change = module:metric("counter", "changes", "events", "CSI state changes", {"csi_state"}); +local count = module:metric("gauge", "state", "sessions", "", { "state" }); module:hook("stream-features", function (event) if event.origin.username then @@ -23,3 +24,23 @@ end module:hook("stanza/"..xmlns_csi..":active", refire_event("csi-client-active")); module:hook("stanza/"..xmlns_csi..":inactive", refire_event("csi-client-inactive")); + +module:hook_global("stats-update", function() + local sessions = prosody.hosts[module.host].sessions; + if not sessions then return end + local active, inactive, flushing = 0, 0, 0; + for _, user_session in pairs(sessions) do + for _, session in pairs(user_session.sessions) do + if session.state == "inactive" then + inactive = inactive + 1; + elseif session.state == "active" then + inactive = inactive + 1; + elseif session.state == "flushing" then + inactive = inactive + 1; + end + end + end + count:with_labels("active"):set(active); + count:with_labels("inactive"):set(inactive); + count:with_labels("flushing"):set(flushing); +end); diff --git a/plugins/mod_s2s.lua b/plugins/mod_s2s.lua index 88b73eba..660b5828 100644 --- a/plugins/mod_s2s.lua +++ b/plugins/mod_s2s.lua @@ -43,6 +43,12 @@ local secure_domains, insecure_domains = local require_encryption = module:get_option_boolean("s2s_require_encryption", true); local stanza_size_limit = module:get_option_integer("s2s_stanza_size_limit", 1024*512, 10000); +local advertised_idle_timeout = 14*60; -- default in all net.server implementations +local network_settings = module:get_option("network_settings"); +if type(network_settings) == "table" and type(network_settings.read_timeout) == "number" then + advertised_idle_timeout = network_settings.read_timeout; +end + local measure_connections_inbound = module:metric( "gauge", "connections_inbound", "", "Established incoming s2s connections", @@ -258,10 +264,15 @@ function module.add_host(module) module:hook("route/remote", route_to_existing_session, -1); module:hook("route/remote", route_to_new_session, -10); module:hook("s2sout-stream-features", function (event) + if not (stanza_size_limit or advertised_idle_timeout) then return end + local limits = event.features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }) if stanza_size_limit then - event.features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }) - :text_tag("max-bytes", string.format("%d", stanza_size_limit)):up(); + limits:text_tag("max-bytes", string.format("%d", stanza_size_limit)); end + if advertised_idle_timeout then + limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout)); + end + limits:up(); end); module:hook_tag("urn:xmpp:bidi", "bidi", function(session, stanza) -- Advertising features on bidi connections where no <stream:features> is sent in the other direction @@ -551,10 +562,16 @@ function stream_callbacks._streamopened(session, attr) end if ( session.type == "s2sin" or session.type == "s2sout" ) or features.tags[1] then - if stanza_size_limit then + if stanza_size_limit or advertised_idle_timeout then + features:reset(); + local limits = features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }); + if stanza_size_limit then + limits:text_tag("max-bytes", string.format("%d", stanza_size_limit)); + end + if advertised_idle_timeout then + limits:text_tag("idle-seconds", string.format("%d", advertised_idle_timeout)); + end features:reset(); - features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" }) - :text_tag("max-bytes", string.format("%d", stanza_size_limit)):up(); end log("debug", "Sending stream features: %s", features); diff --git a/plugins/mod_s2s_bidi.lua b/plugins/mod_s2s_bidi.lua index 22415293..8588ce59 100644 --- a/plugins/mod_s2s_bidi.lua +++ b/plugins/mod_s2s_bidi.lua @@ -12,10 +12,15 @@ local xmlns_bidi = "urn:xmpp:bidi"; local require_encryption = module:get_option_boolean("s2s_require_encryption", true); +local offers_sent = module:metric("counter", "offers_sent", "", "Bidirectional connection offers sent", {}); +local offers_recv = module:metric("counter", "offers_recv", "", "Bidirectional connection offers received", {}); +local offers_taken = module:metric("counter", "offers_taken", "", "Bidirectional connection offers taken", {}); + module:hook("s2s-stream-features", function(event) local origin, features = event.origin, event.features; if origin.type == "s2sin_unauthed" and (not require_encryption or origin.secure) then features:tag("bidi", { xmlns = xmlns_bidi_feature }):up(); + offers_sent:with_labels():add(1); end end); @@ -28,6 +33,7 @@ module:hook_tag("http://etherx.jabber.org/streams", "features", function (sessio local request_bidi = st.stanza("bidi", { xmlns = xmlns_bidi }); module:fire_event("s2sout-stream-features", { origin = session, features = request_bidi }); session.sends2s(request_bidi); + offers_taken:with_labels():add(1); end end end, 200); @@ -36,6 +42,7 @@ module:hook_tag("urn:xmpp:bidi", "bidi", function(session) if session.type == "s2sin_unauthed" and (not require_encryption or session.secure) then session.log("debug", "Requested bidirectional stream"); session.outgoing = true; + offers_recv:with_labels():add(1); return true; end end); diff --git a/spec/util_xtemplate_spec.lua b/spec/util_xtemplate_spec.lua index 4561378e..421be43f 100644 --- a/spec/util_xtemplate_spec.lua +++ b/spec/util_xtemplate_spec.lua @@ -38,6 +38,10 @@ describe("util.xtemplate", function () x:reset(); assert.same("12345", xtemplate.render("{foo/bar|each(i){{#}}}", x)); end) + it("handles missing inputs", function () + local x = st.stanza("root"); + assert.same("", xtemplate.render("{foo/bar|each(i){{#}}}", x)); + end) end) end) end) diff --git a/teal-src/prosody/util/xtemplate.tl b/teal-src/prosody/util/xtemplate.tl index 9b6b678b..84003051 100644 --- a/teal-src/prosody/util/xtemplate.tl +++ b/teal-src/prosody/util/xtemplate.tl @@ -54,7 +54,10 @@ local function render(template : string, root : st.stanza_t, escape : escape_t, if tmpl then tmpl = s_sub(tmpl, 2, -2); end if args then args = s_sub(args, 2, -2); end - if func == "each" and tmpl and st.is_stanza(value) then + if func == "each" and tmpl then + if not st.is_stanza(value) then + return pre_blank..post_blank; + end if not args then value, args = root, path; end local ns, name = s_match(args, "^(%b{})(.*)$"); if ns then ns = s_sub(ns, 2, -2); else name, ns = args, nil; end diff --git a/util/sslconfig.lua b/util/sslconfig.lua index 7b0ed34a..01a8adb5 100644 --- a/util/sslconfig.lua +++ b/util/sslconfig.lua @@ -84,8 +84,18 @@ 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; + +function finalisers.dhparam(value, config) + if type(value) == "string" then + if value:sub(1, 10) == "-----BEGIN" then + -- literal value + return value; + else + -- assume a filename + return resolve_path(config._basedir, value); + end + end +end -- protocol = "x" should enable only that protocol -- protocol = "x+" should enable x and later versions diff --git a/util/xtemplate.lua b/util/xtemplate.lua index 56413012..e23b1a01 100644 --- a/util/xtemplate.lua +++ b/util/xtemplate.lua @@ -39,7 +39,8 @@ local function render(template, root, escape, filters) if tmpl then tmpl = s_sub(tmpl, 2, -2); end if args then args = s_sub(args, 2, -2); end - if func == "each" and tmpl and st.is_stanza(value) then + if func == "each" and tmpl then + if not st.is_stanza(value) then return pre_blank .. post_blank end if not args then value, args = root, path; end local ns, name = s_match(args, "^(%b{})(.*)$"); if ns then |