Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package prosody for openSUSE:Factory checked in at 2026-09-28 10:40:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/prosody (Old) and /work/SRC/openSUSE:Factory/.prosody.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "prosody" Mon Sep 28 10:40:10 2026 rev:45 rq:1380331 version:13.0.7 Changes: -------- --- /work/SRC/openSUSE:Factory/prosody/prosody.changes 2026-06-03 20:28:29.872330629 +0200 +++ /work/SRC/openSUSE:Factory/.prosody.new.383539/prosody.changes 2026-09-28 10:40:38.590349160 +0200 @@ -1,0 +2,20 @@ +Fri Sep 25 05:54:36 UTC 2026 - Michael Vetter <[email protected]> + +- Update to 13.0.7: + * rostermanager, mod_roster: Include ‘approved’ attribute in roster items + * util.startup: Always apply umask + * Update TLS profile data from Mozilla/TLSRef + * net.server_epoll: Disable read timeouts on server sockets + * net.unbound: Reset and apply new config on reload + * net.unbound: Simplify by removing cancel() + * util.poll: Reject file descriptors outside of FD_SETSIZE in all methods + * net.websocket: Release connection and input state on disconnect + * net.server_select: Release queued strings on forced close + * net.unbound: Let in-flight queries complete after re-initialization + * net.server_epoll: Release pending write buffers on destruction + * net.server_event: Release retained state on destruction + * net.server_event: Fix incorrect flag logic for watchfd handles + * prosodyctl check: Validate tls_profile and tls_profile_version + * See https://blog.prosody.im/prosody-13.0.7-released + +------------------------------------------------------------------- Old: ---- prosody-13.0.6.tar.gz prosody-13.0.6.tar.gz.asc New: ---- prosody-13.0.7.tar.gz prosody-13.0.7.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ prosody.spec ++++++ --- /var/tmp/diff_new_pack.aZ6X0h/_old 2026-09-28 10:40:39.753397930 +0200 +++ /var/tmp/diff_new_pack.aZ6X0h/_new 2026-09-28 10:40:39.754397972 +0200 @@ -18,7 +18,7 @@ %define _piddir /run Name: prosody -Version: 13.0.6 +Version: 13.0.7 Release: 0 Summary: Communications server for Jabber/XMPP License: MIT ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.aZ6X0h/_old 2026-09-28 10:40:39.841401620 +0200 +++ /var/tmp/diff_new_pack.aZ6X0h/_new 2026-09-28 10:40:39.851402040 +0200 @@ -1,6 +1,6 @@ -mtime: 1780466263 -commit: 96f6b9288d6a2f05d732feffc5e482b5a105dd1b0fc8d7ebfafa5b60f9420f9a +mtime: 1790315810 +commit: 93a19189edd04f65298e2d7b74e7d7ec56b50a4e7104a02c40935d3f28511683 url: https://src.opensuse.org/lua/prosody -revision: 96f6b9288d6a2f05d732feffc5e482b5a105dd1b0fc8d7ebfafa5b60f9420f9a +revision: 93a19189edd04f65298e2d7b74e7d7ec56b50a4e7104a02c40935d3f28511683 projectscmsync: https://src.opensuse.org/lua/_ObsPrj.git ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-09-25 07:56:50.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ prosody-13.0.6.tar.gz -> prosody-13.0.7.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/.hg_archival.txt new/prosody-13.0.7/.hg_archival.txt --- old/prosody-13.0.6/.hg_archival.txt 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/.hg_archival.txt 2026-09-23 18:44:22.389430863 +0200 @@ -1,4 +1,4 @@ repo: 3e3171b59028ee70122cfec6ecf98f518f946b59 -node: 8eac8de97bca1ed5debe7a1366ab4414e4ea4dfe +node: dab08c582cf273dfdc72a524f621ccbb496a34d9 branch: 13.0 -tag: 13.0.6 +tag: 13.0.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/core/certmanager.lua new/prosody-13.0.7/core/certmanager.lua --- old/prosody-13.0.6/core/certmanager.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/core/certmanager.lua 2026-09-23 18:44:22.389430863 +0200 @@ -12,6 +12,8 @@ local tls = require "prosody.net.tls_luasec"; local stat = require "lfs".attributes; +local tlsref = require "prosody.util.tlsref"; + local x509 = require "prosody.util.x509"; local lfs = require "lfs"; @@ -216,84 +218,6 @@ 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 - modern = { - protocol = "tlsv1_3"; - options = { cipher_server_preference = false }; - ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these - curveslist = { "X25519"; "prime256v1"; "secp384r1" }; - ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; - }; - intermediate = { - protocol = "tlsv1_2+"; - dhparam = ffdhe2048; - options = { cipher_server_preference = false }; - ciphers = { - "ECDHE-ECDSA-AES128-GCM-SHA256"; - "ECDHE-RSA-AES128-GCM-SHA256"; - "ECDHE-ECDSA-AES256-GCM-SHA384"; - "ECDHE-RSA-AES256-GCM-SHA384"; - "ECDHE-ECDSA-CHACHA20-POLY1305"; - "ECDHE-RSA-CHACHA20-POLY1305"; - "DHE-RSA-AES128-GCM-SHA256"; - "DHE-RSA-AES256-GCM-SHA384"; - "DHE-RSA-CHACHA20-POLY1305"; - }; - curveslist = { "X25519"; "prime256v1"; "secp384r1" }; - ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; - }; - old = { - protocol = "tlsv1+"; - dhparam = nil; -- openssl dhparam 1024 - options = { cipher_server_preference = true }; - ciphers = { - "ECDHE-ECDSA-AES128-GCM-SHA256"; - "ECDHE-RSA-AES128-GCM-SHA256"; - "ECDHE-ECDSA-AES256-GCM-SHA384"; - "ECDHE-RSA-AES256-GCM-SHA384"; - "ECDHE-ECDSA-CHACHA20-POLY1305"; - "ECDHE-RSA-CHACHA20-POLY1305"; - "DHE-RSA-AES128-GCM-SHA256"; - "DHE-RSA-AES256-GCM-SHA384"; - "DHE-RSA-CHACHA20-POLY1305"; - "ECDHE-ECDSA-AES128-SHA256"; - "ECDHE-RSA-AES128-SHA256"; - "ECDHE-ECDSA-AES128-SHA"; - "ECDHE-RSA-AES128-SHA"; - "ECDHE-ECDSA-AES256-SHA384"; - "ECDHE-RSA-AES256-SHA384"; - "ECDHE-ECDSA-AES256-SHA"; - "ECDHE-RSA-AES256-SHA"; - "DHE-RSA-AES128-SHA256"; - "DHE-RSA-AES256-SHA256"; - "AES128-GCM-SHA256"; - "AES256-GCM-SHA384"; - "AES128-SHA256"; - "AES256-SHA256"; - "AES128-SHA"; - "AES256-SHA"; - "DES-CBC3-SHA"; - }; - curveslist = { "X25519"; "prime256v1"; "secp384r1" }; - ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; - }; -}; - - if tls.features.curves then for i = #core_defaults.curveslist, 1, -1 do if not tls.features.curves[ core_defaults.curveslist[i] ] then @@ -321,12 +245,33 @@ -- We can't read the password interactively when daemonized password = function() log("error", "Encrypted certificate for %s requires 'ssl' 'password' to be set in config", host); end; }); - local profile = configmanager.get("*", "tls_profile") or "intermediate"; - if mozilla_ssl_configs[profile] then - cfg:apply(mozilla_ssl_configs[profile]); - elseif profile ~= "legacy" then - log("error", "Invalid value for 'tls_profile': expected one of \"modern\", \"intermediate\" (default), \"old\" or \"legacy\" but got %q", profile); - return nil, "Invalid configuration, 'tls_profile' had an unknown value."; + local profile_version = configmanager.get("*", "tls_profile_version"); + local profile_name = configmanager.get("*", "tls_profile"); + + if profile_name ~= "legacy" then + local tls_profile, tls_profile_err = tlsref.get_profile(profile_version, profile_name); + if not tls_profile then + if tls_profile_err == "unknown-version" then + log( + "error", + "Invalid value %q for 'tls_profile_version': expected one of: \"%s\" or \"latest\"", + profile_version, + tlsref.get_valid_versions():concat("\", \"") + ); + return nil, "Invalid configuration, 'tls_profile_version' had an unknown value."; + elseif tls_profile_err == "unknown-profile" then + log( + "error", + "Invalid value %q for 'tls_profile': expected one of \"%s\" or \"legacy\" (default: %q)", + profile_name, + tlsref.get_valid_profile_names(profile_version):concat("\", \""), + tlsref.get_default_profile_name(profile_version) + ); + return nil, "Invalid configuration, 'tls_profile' had an unknown value."; + end + else + cfg:apply(tls_profile); + end end cfg:apply(global_ssl_config); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/core/features.lua new/prosody-13.0.7/core/features.lua --- old/prosody-13.0.6/core/features.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/core/features.lua 2026-09-23 18:44:22.389430863 +0200 @@ -37,5 +37,8 @@ -- SIGUSR1 and 2 events "signal-events"; + + -- util.human.io.simple_version_compare + "simple-version-compare"; }; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/core/rostermanager.lua new/prosody-13.0.7/core/rostermanager.lua --- old/prosody-13.0.6/core/rostermanager.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/core/rostermanager.lua 2026-09-23 18:44:22.389430863 +0200 @@ -67,7 +67,13 @@ local stanza = st.iq({type="set", id=new_id()}); stanza:tag("query", {xmlns = "jabber:iq:roster", ver = tostring(roster[false].version or "1") }); if item then - stanza:tag("item", {jid = jid, subscription = item.subscription, name = item.name, ask = item.ask}); + stanza:tag("item", { + jid = jid; + subscription = item.subscription; + name = item.name; + ask = item.ask; + approved = item.approved; + }); for group in pairs(item.groups) do stanza:tag("group"):text(group):up(); end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/net/server_epoll.lua new/prosody-13.0.7/net/server_epoll.lua --- old/prosody-13.0.6/net/server_epoll.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/net/server_epoll.lua 2026-09-23 18:44:22.389430863 +0200 @@ -541,6 +541,10 @@ self:onconnect(); if not self.conn then return nil, "no-conn"; end -- could have been closed in onconnect self:on("predrain"); + if not self.conn then -- The predrain handler may have destroyed the connection. + self._writing = nil; + return nil, "no-conn"; + end local buffer = self.writebuffer or ""; -- Naming things ... s/data/slice/ ? local data = buffer:sub(1, cfg.max_send_chunk); @@ -593,6 +597,7 @@ -- Add data to write buffer and set flag for wanting to write function interface:write(data) + if not self.conn then return nil, "closed"; end local buffer = self.writebuffer; -- (nil) -> save string -- (string) -> convert to buffer (3 tables!) @@ -709,6 +714,7 @@ self.on = noop; self.conn:close(); self.conn = nil; + self.writebuffer = nil; end function interface:ssl() @@ -1024,6 +1030,7 @@ listeners = listeners; read_size = config and config.read_size; onreadable = interface.onacceptable; + setreadtimeout = noop; tls_ctx = config and config.tls_ctx; tls_direct = config and config.tls_direct; hosts = config and config.sni_hosts; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/net/server_event.lua new/prosody-13.0.7/net/server_event.lua --- old/prosody-13.0.6/net/server_event.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/net/server_event.lua 2026-09-23 18:44:22.389430863 +0200 @@ -229,6 +229,8 @@ return true end function interface_mt:_destroy() -- close this interface + events and call last listener + if self._destroyed then return true; end + self._destroyed = true; debug( "closing client with id:", self.id, self.fatalerror ) self:_lock( true, true, true ) -- first of all, lock the interface to avoid further actions local _ @@ -248,11 +250,16 @@ self.eventread, self.eventwrite = nil, nil self.eventstarthandshake, self.eventhandshake, self.eventclose = nil, nil, nil self.readcallback, self.writecallback = nil, nil + self.eventconnect, self.eventsession = nil, nil + self.eventwritetimeout, self.eventreadtimeout = nil, nil + self.startsslcallback = nil else self.conn:close( ) self.eventread, self.eventclose = nil, nil self.interface, self.readcallback = nil, nil end + self.conn = nil + self.writebuffer, self.writebufferlen = nil, 0 interfacelist[ self ] = nil return true end @@ -583,6 +590,7 @@ end end interface:onpredrain(); + if interface._destroyed then return -1; end interface.writebuffer = { t_concat(interface.writebuffer) } local succ, err, byte = interface.conn:send( interface.writebuffer[1], 1, interface.writebufferlen ) --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte ) @@ -590,6 +598,7 @@ interface.writebuffer[1] = nil interface.writebufferlen = 0 interface:ondrain(); + if interface._destroyed then return -1; end if interface.fatalerror then debug "closing client after writing" interface:_close() -- close interface if needed @@ -654,6 +663,7 @@ interface.eventreadtimeout = nil end end + if interface._destroyed then return -1; end local buffer, err, part = interface.conn:receive( interface._pattern ) -- receive buffer with "pattern" --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) ) buffer = buffer or part diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/net/server_select.lua new/prosody-13.0.7/net/server_select.lua --- old/prosody-13.0.6/net/server_select.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/net/server_select.lua 2026-09-23 18:44:22.389430863 +0200 @@ -392,8 +392,12 @@ handler.force_close = function ( self, err ) if bufferqueuelen ~= 0 then out_put("server.lua: discarding unwritten data for ", tostring(ip), ":", tostring(clientport)) + for i = 1, bufferqueuelen do + bufferqueue[i] = nil; + end bufferqueuelen = 0; end + bufferlen = 0; return self:close(err); end handler.close = function( self, err ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/net/unbound.lua new/prosody-13.0.7/net/unbound.lua --- old/prosody-13.0.6/net/unbound.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/net/unbound.lua 2026-09-23 18:44:22.389430863 +0200 @@ -19,6 +19,7 @@ local libunbound = require"lunbound"; local promise = require"prosody.util.promise"; local new_id = require "prosody.util.id".short; +local timer = require "prosody.util.timer"; local dns_utils = require"prosody.util.dns"; local classes, types, errors = dns_utils.classes, dns_utils.types, dns_utils.errors; @@ -42,13 +43,6 @@ end local unbound_config; -if prosody then - local config = require"prosody.core.configmanager"; - unbound_config = add_defaults(config.get("*", "unbound")); - prosody.events.add_handler("config-reloaded", function() - unbound_config = add_defaults(config.get("*", "unbound")); - end); -end -- Note: libunbound will default to using root hints if resolvconf is unset local function connect_server(unbound, server) @@ -65,9 +59,6 @@ unbound = libunbound.new(unbound_config); server_conn = connect_server(unbound, net_server); end -if prosody then - prosody.events.add_handler("server-started", initialize); -end local answer_mt = { __tostring = function(self) @@ -92,8 +83,6 @@ end; }; -local waiting_queries = {}; - local function prep_answer(a) if not a then return end local status = errors[a.rcode]; @@ -126,11 +115,9 @@ local ntype, nclass = types[qtype], classes[qclass]; local m; - local ret; local log_query = logger.init("unbound.query"..new_id()); local function callback_wrapper(a, err) m(); - waiting_queries[ret] = nil; if a then prep_answer(a); log_query("debug", "Results for %s %s %s: %s (%s)", qname, qclass, qtype, a.rcode == 0 and (#a .. " items") or a.status, @@ -143,11 +130,8 @@ end log_query("debug", "Resolve %s %s %s", qname, qclass, qtype); m = measure(qclass, qtype); - local err; - ret, err = unbound:resolve_async(callback_wrapper, qname, ntype, nclass); - if ret then - waiting_queries[ret] = callback; - else + local ret, err = unbound:resolve_async(callback_wrapper, qname, ntype, nclass); + if not ret then log_query("error", "Resolver error: %s", err); end return ret, err; @@ -163,35 +147,42 @@ return prep_answer(a); end -local function cancel(id) - local cb = waiting_queries[id]; - unbound:cancel(id); - if cb then - cb(nil, "canceled"); - waiting_queries[id] = nil; - end - return true; -end - -- Reinitiate libunbound context, drops cache local function purge() - for id in pairs(waiting_queries) do cancel(id); end - if server_conn then server_conn:close(); end + if server_conn then + local old_unbound, old_server_conn = unbound, server_conn; + timer.add_task(30, function() + old_server_conn:close(); + if old_unbound.cancelall then + old_unbound:cancelall(); + end + end) + end initialize(); return true; end +if prosody then + local config = require"prosody.core.configmanager"; + unbound_config = add_defaults(config.get("*", "unbound")); + prosody.events.add_handler("config-reloaded", function() + unbound_config = add_defaults(config.get("*", "unbound")); + purge(); + end); + prosody.events.add_handler("server-started", initialize); +end + local function not_implemented() error "not implemented"; end -- Public API local _M = { lookup = lookup; - cancel = cancel; + cancel = not_implemented; new_async_socket = not_implemented; dns = { lookup = lookup_sync; - cancel = cancel; + cancel = not_implemented; cache = noop; socket_wrapper_set = noop; settimeout = noop; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/net/websocket.lua new/prosody-13.0.7/net/websocket.lua --- old/prosody-13.0.6/net/websocket.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/net/websocket.lua 2026-09-23 18:44:22.389430863 +0200 @@ -30,6 +30,8 @@ s.close_timer = nil; end s.readyState = 3; + s.conn = nil; + s.readbuffer, s.databuffer = "", nil; if s.close_code == nil and s.onerror then s:onerror(err); end if s.onclose then s:onclose(s.close_code, s.close_message or err); end end @@ -40,8 +42,9 @@ local function fail(s, code, reason) log("warn", "WebSocket connection failed, closing. %d %s", code, reason); + local conn = s.conn; s:close(code, reason); - s.conn:close(); + conn:close(); return false end @@ -136,6 +139,7 @@ self.readyState = 2; local conn = self.conn; conn:write(frames.build_close(code, reason, true)); + if self.readyState == 3 then return; end -- Do not close socket straight away, wait for acknowledgement from server. self.close_timer = timer.add_task(close_timeout, close_timeout_cb, self); elseif self.readyState == 2 then diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/plugins/mod_roster.lua new/prosody-13.0.7/plugins/mod_roster.lua --- old/prosody-13.0.6/plugins/mod_roster.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/plugins/mod_roster.lua 2026-09-23 18:44:22.389430863 +0200 @@ -48,6 +48,7 @@ roster:tag("item", { jid = jid, subscription = item.subscription, + approved = item.approved, ask = item.ask, name = item.name, }); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/prosody.release new/prosody-13.0.7/prosody.release --- old/prosody-13.0.6/prosody.release 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/prosody.release 2026-09-23 18:44:22.389430863 +0200 @@ -1 +1 @@ -13.0.6 +13.0.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/spec/util_human_io_spec.lua new/prosody-13.0.7/spec/util_human_io_spec.lua --- old/prosody-13.0.6/spec/util_human_io_spec.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/spec/util_human_io_spec.lua 2026-09-23 18:44:22.389430863 +0200 @@ -100,6 +100,79 @@ return assert.is_nil(human_io.parse_duration_lax("two weeks"), "\"2 weeks\" -> nil"); end); end); + + describe("simple_version_compare", function () + local version_compare = human_io.simple_version_compare; + + local function cmp_version(a, b, exp) + assert.equal(exp, version_compare(a, b), ("%q < %q"):format(a, b)); + end + + it("orders versions with differing major numbers", function () + cmp_version("1.0", "2.0", true); + cmp_version("2.0", "1.0", false); + end); + + it("orders versions with differing minor numbers", function () + cmp_version("1.0", "1.1", true); + cmp_version("1.1", "1.0", false); + end); + + it("treats a shorter version as less than a longer one sharing the same prefix", function () + cmp_version("1.0", "1.0.1", true); + cmp_version("1.0.1", "1.0", false); + + cmp_version("1", "1.0", true); + cmp_version("1", "1.1", true); + + cmp_version("1.0.0", "1.0.0.1", true); + end); + + it("compares components left-to-right regardless of how many components there are", function () + cmp_version("1.0.1", "1.1", true); + cmp_version("1.1", "1.0.1", false); + + cmp_version("1.0.1", "1.1.0", true); + cmp_version("1.1.0", "1.0.1", false); + + cmp_version("1.0.0.1", "1.0.1", true); + end); + + it("never considers a version less than itself", function () + cmp_version("0", "0", false); + cmp_version("1.0", "1.0", false); + cmp_version("1.2.3", "1.2.3", false); + cmp_version("foo", "foo", false); + end); + + it("compares components numerically", function () + cmp_version("1.9", "1.10", true); + cmp_version("1.10", "1.9", false); + + cmp_version("1.9.9", "1.10.0", true); + + cmp_version("2.0", "10.0", true); + cmp_version("10.0", "2.0", false); + + cmp_version("1.2", "1.10", true); + + cmp_version("1.99", "1.100", true); + cmp_version("1.100", "1.99", false); + end); + + it("handles leading zeros in numeric components", function () + cmp_version("1.02", "1.10", true); + cmp_version("1.10", "1.02", false); + end); + + it("treats an empty or non-numeric string as a version with no components", function () + cmp_version("", "1.0", true); + cmp_version("1.0", "", false); + cmp_version("foo", "1.0", true); + cmp_version("1.0", "foo", false); + cmp_version("", "", false); + end); + end); end); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/spec/util_tlsref_spec.lua new/prosody-13.0.7/spec/util_tlsref_spec.lua --- old/prosody-13.0.6/spec/util_tlsref_spec.lua 1970-01-01 01:00:00.000000000 +0100 +++ new/prosody-13.0.7/spec/util_tlsref_spec.lua 2026-09-23 18:44:22.389430863 +0200 @@ -0,0 +1,168 @@ +local tlsref = require "prosody.util.tlsref"; + +describe("util.tlsref", function() + local version_compare = require "prosody.util.human.io".simple_version_compare; + + describe("default version", function() + it("is not greater than latest version", function () + local default_version = tlsref.get_default_version(); + local latest_version = tlsref.get_latest_version(); + assert.equal(false, version_compare(latest_version, default_version)); + end); + + it("is a known version", function () + assert.is_table(tlsref.get_profile(tlsref.get_default_version())); + end); + end); + + describe("latest version", function () + it("is a known version", function () + assert.is_table(tlsref.get_profile("latest")); + end); + + it("is the first entry of get_valid_versions()", function () + local versions = tlsref.get_valid_versions(); + assert.equal(tlsref.get_latest_version(), versions[1]); + end); + end); + + describe("get_valid_versions()", function () + it("returns versions in descending order", function () + local versions = tlsref.get_valid_versions(); + assert.is_true(#versions >= 1); + for i = 1, #versions-1 do + -- versions[i] must sort before versions[i+1] + assert.equal(false, version_compare(versions[i], versions[i+1])); + end + end); + + it("contains the default version", function () + local found = false; + for _, v in ipairs(tlsref.get_valid_versions()) do + if v == tlsref.get_default_version() then found = true; end + end + assert.is_true(found); + end); + end); + + describe("get_valid_profile_names()", function () + it("returns an error for unknown versions", function () + local names, err = tlsref.get_valid_profile_names("0.0"); + assert.is_nil(names); + assert.equal("unknown-version", err); + end); + + it("uses the default version when none is given", function () + assert.same( + tlsref.get_valid_profile_names(tlsref.get_default_version()), + tlsref.get_valid_profile_names() + ); + end); + + it("accepts 'latest' as a version", function () + assert.same( + tlsref.get_valid_profile_names(tlsref.get_latest_version()), + tlsref.get_valid_profile_names("latest") + ); + end); + + it("lists the most-preferred profile first", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + local names = tlsref.get_valid_profile_names(version); + assert.equal("modern", names[1], + ("most-preferred profile should be first for version %s"):format(version)); + end + end); + end); + + describe("get_profile()", function () + it("returns the default profile of the default version when called with no arguments", function () + assert.equal( + tlsref.get_profile(tlsref.get_default_version(), tlsref.get_default_profile_name()), + tlsref.get_profile() + ); + end); + + it("resolves 'latest' to the latest version", function () + assert.equal( + tlsref.get_profile(tlsref.get_latest_version()), + tlsref.get_profile("latest") + ); + end); + + it("returns an error for unknown versions", function () + local profile, err = tlsref.get_profile("0.0"); + assert.is_nil(profile); + assert.equal("unknown-version", err); + end); + + it("returns an error for unknown profile names", function () + local profile, err = tlsref.get_profile(nil, "no-such-profile"); + assert.is_nil(profile); + assert.equal("unknown-profile", err); + end); + + it("returns an error for profiles absent from a given version", function () + -- 'old' was dropped from the 6.0 guidelines + local profile, err = tlsref.get_profile("6.0", "old"); + assert.is_nil(profile); + assert.equal("unknown-profile", err); + end); + + it("resolves every advertised version/profile combination", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + for _, name in ipairs(tlsref.get_valid_profile_names(version)) do + local profile, err = tlsref.get_profile(version, name); + assert.is_table(profile, + ("get_profile(%q, %q) failed: %s"):format(version, name, err)); + end + end + end); + end); + + describe("get_default_profile_name()", function () + it("returns an error for unknown versions", function () + local name, err = tlsref.get_default_profile_name("0.0"); + assert.is_nil(name); + assert.equal("unknown-version", err); + end); + + it("accepts 'latest' as a version", function () + assert.is_string(tlsref.get_default_profile_name("latest")); + end); + + it("returns a profile name valid for every version", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + local name = tlsref.get_default_profile_name(version); + assert.is_string(name); + assert.is_table(tlsref.get_profile(version, name), + ("default profile %q missing from version %s"):format(name, version)); + end + end); + end); + + describe("profile contents", function () + it("every profile specifies a TLS protocol version", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + for _, name in ipairs(tlsref.get_valid_profile_names(version)) do + local profile = tlsref.get_profile(version, name); + assert.is_string(profile.protocol); + assert.truthy(profile.protocol:match("^tlsv1"), + ("unexpected protocol %q in %s/%s"):format(profile.protocol, version, name)); + end + end + end); + + it("every profile specifies TLS 1.3 ciphersuites and curves", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + for _, name in ipairs(tlsref.get_valid_profile_names(version)) do + local profile = tlsref.get_profile(version, name); + assert.is_table(profile.ciphersuites); + assert.is_true(#profile.ciphersuites > 0); + assert.is_table(profile.curveslist); + assert.is_true(#profile.curveslist > 0); + end + end + end); + end); +end); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/util/human/io.lua new/prosody-13.0.7/util/human/io.lua --- old/prosody-13.0.6/util/human/io.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/util/human/io.lua 2026-09-23 18:44:22.389430863 +0200 @@ -222,6 +222,29 @@ return tonumber(n) * ( multipliers_lax[m] or 1 ); end +-- Compares simple version strings (numeric only, not semver compatible) +-- (can be passed to table.sort) +local function simple_version_compare(a, b) + local a_f, a_s, a_part = a:gmatch("%d+"); + local b_f, b_s, b_part = b:gmatch("%d+"); + + a_part = a_f(a_s, a_part); + b_part = b_f(b_s, b_part); + + while a_part and b_part do + local an, bn = tonumber(a_part), tonumber(b_part); + if an ~= bn then + return an < bn; + end + a_part = a_f(a_s, a_part); + b_part = b_f(b_s, b_part); + end + + -- return true (a is lower) if b is exhausted + -- otherwise, they are equal or b is longer/greater (-> return false) + return b_part ~= nil; +end + return { getchar = getchar; getline = getline; @@ -237,4 +260,5 @@ table = new_table; parse_duration = parse_duration; parse_duration_lax = parse_duration_lax; + simple_version_compare = simple_version_compare; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/util/prosodyctl/check.lua new/prosody-13.0.7/util/prosodyctl/check.lua --- old/prosody-13.0.6/util/prosodyctl/check.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/util/prosodyctl/check.lua 2026-09-23 18:44:22.389430863 +0200 @@ -464,6 +464,7 @@ "statistics_interval", "tcp_keepalives", "tls_profile", + "tls_profile_version", "trusted_proxies", "umask", "use_dane", @@ -726,6 +727,26 @@ end do + local tlsref = require "prosody.util.tlsref"; + local tls_profile_name = configmanager.get("*", "tls_profile"); + local tls_profile_version = configmanager.get("*", "tls_profile_version"); + local profile, err = tlsref.get_profile(tls_profile_version, tls_profile_name); + if not profile then + print(""); + print(" TLS profile selection:"); + if err == "unknown-version" then + print((" tls_profile_version is set to an unknown value (%q)"):format(tls_profile_version)); + print((" Valid values: \"%s\""):format(tlsref.get_valid_versions():concat("\", \""))); + elseif err == "unknown-profile" then + print((" tls_profile is set to an unknown value (%q)"):format(tls_profile_name)); + print((" Valid values: \"%s\""):format(tlsref.get_valid_profile_names():concat("\", \""))); + else + print((" Unknown error loading the configured profile: %s"):format(err)); + end + end + end + + do local registration_enabled_hosts = {}; for host in enabled_hosts() do local host_modules, component = modulemanager.get_modules_for_host(host); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/util/startup.lua new/prosody-13.0.7/util/startup.lua --- old/prosody-13.0.6/util/startup.lua 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/util/startup.lua 2026-09-23 18:44:22.389430863 +0200 @@ -634,8 +634,6 @@ end end - -- Set our umask to protect data files - pposix.umask(config.get("*", "umask") or "027"); pposix.setenv("HOME", prosody.paths.data); pposix.setenv("PROSODY_CONFIG", prosody.config_file); else @@ -725,9 +723,9 @@ end end -function startup.posix_umask() +function startup.set_umask() if prosody.platform ~= "posix" then return end - local pposix = require "prosody.util.pposix"; + local pposix = check_posix(); local umask = config.get("*", "umask") or "027"; pposix.umask(umask); end @@ -936,6 +934,7 @@ startup.chdir(); startup.read_version(); startup.switch_user(); + startup.set_umask(); startup.check_dependencies(); startup.log_startup_warnings(); startup.check_unwriteable(); @@ -964,6 +963,7 @@ startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); + startup.set_umask(); startup.add_global_prosody_functions(); startup.read_version(); startup.log_greeting(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/util/tlsref.lua new/prosody-13.0.7/util/tlsref.lua --- old/prosody-13.0.6/util/tlsref.lua 1970-01-01 01:00:00.000000000 +0100 +++ new/prosody-13.0.7/util/tlsref.lua 2026-09-23 18:44:22.389430863 +0200 @@ -0,0 +1,232 @@ +local array = require "prosody.util.array"; +local it = require "prosody.util.iterators"; +local version_compare = require "prosody.util.human.io".simple_version_compare; + +local latest_version = "6.0" +local default_version = "5.7"; +local default_profile = "intermediate"; -- should exist in all profiles + +-- 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 profiles = { + -- https://wiki.mozilla.org/Security/Server_Side_TLS + ["6.0"] = { + -- Version 6.0 as of 2026-04-08 + modern = { + protocol = "tlsv1_3"; + options = { cipher_server_preference = false }; + ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + intermediate = { + protocol = "tlsv1_2+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = false }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + }; + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + }; + + ["5.8"] = { + -- Version 5.8 as of 2026-04-08 + modern = { + protocol = "tlsv1_3"; + options = { cipher_server_preference = false }; + ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + intermediate = { + protocol = "tlsv1_2+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = false }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + }; + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + old = { + protocol = "tlsv1+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = true }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + "ECDHE-ECDSA-AES128-SHA256"; + "ECDHE-RSA-AES128-SHA256"; + "ECDHE-ECDSA-AES128-SHA"; + "ECDHE-RSA-AES128-SHA"; + "ECDHE-ECDSA-AES256-SHA384"; + "ECDHE-RSA-AES256-SHA384"; + "ECDHE-ECDSA-AES256-SHA"; + "ECDHE-RSA-AES256-SHA"; + "AES128-GCM-SHA256"; + "AES256-GCM-SHA384"; + "AES128-SHA256"; + "AES256-SHA256"; + "AES128-SHA"; + "AES256-SHA"; + "DES-CBC3-SHA"; + }; + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + }; + + ["5.7"] = { + -- Version 5.7 as of 2023-07-09 + modern = { + protocol = "tlsv1_3"; + options = { cipher_server_preference = false }; + ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these + curveslist = { "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + intermediate = { + protocol = "tlsv1_2+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = false }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + "DHE-RSA-AES128-GCM-SHA256"; + "DHE-RSA-AES256-GCM-SHA384"; + "DHE-RSA-CHACHA20-POLY1305"; + }; + curveslist = { "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + old = { + protocol = "tlsv1+"; + dhparam = nil; -- openssl dhparam 1024 + options = { cipher_server_preference = true }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + "DHE-RSA-AES128-GCM-SHA256"; + "DHE-RSA-AES256-GCM-SHA384"; + "DHE-RSA-CHACHA20-POLY1305"; + "ECDHE-ECDSA-AES128-SHA256"; + "ECDHE-RSA-AES128-SHA256"; + "ECDHE-ECDSA-AES128-SHA"; + "ECDHE-RSA-AES128-SHA"; + "ECDHE-ECDSA-AES256-SHA384"; + "ECDHE-RSA-AES256-SHA384"; + "ECDHE-ECDSA-AES256-SHA"; + "ECDHE-RSA-AES256-SHA"; + "DHE-RSA-AES128-SHA256"; + "DHE-RSA-AES256-SHA256"; + "AES128-GCM-SHA256"; + "AES256-GCM-SHA384"; + "AES128-SHA256"; + "AES256-SHA256"; + "AES128-SHA"; + "AES256-SHA"; + "DES-CBC3-SHA"; + }; + curveslist = { "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + }; +}; + +local function get_valid_versions() + return array.collect(it.keys(profiles)):sort(version_compare):reverse(); +end + +local pref_order = { modern = 100, intermediate = 50, old = 0 }; + +local function sort_profile_by_pref(a, b) + local pref_a, pref_b = pref_order[a] or 0, pref_order[b] or 0; + return pref_a > pref_b; -- Best first +end + +local function get_valid_profile_names(version) + if version == "latest" then + version = latest_version; + end + local version_profiles = profiles[version or default_version]; + if not version_profiles then + return nil, "unknown-version"; + end + return array.collect(it.keys(version_profiles)):sort(sort_profile_by_pref); +end + +local function get_profile(version, profile_name) + if version == "latest" then + version = latest_version; + end + local version_profiles = profiles[version or default_version]; + if not version_profiles then + return nil, "unknown-version"; + end + local profile = version_profiles[profile_name or default_profile]; + if not profile then + return nil, "unknown-profile"; + end + return profile; +end + +local function get_default_version() + return default_version; +end + +local function get_latest_version() + return latest_version; +end + +local function get_default_profile_name(version) + -- Default profile name is currently the same across all versions, + -- but can't guarantee this in the future - ensure valid version + if version ~= "latest" and not profiles[version or default_version] then + return nil, "unknown-version"; + end + return default_profile; +end + +return { + profiles = profiles; + get_default_version = get_default_version; + get_latest_version = get_latest_version; + get_default_profile_name = get_default_profile_name; + get_valid_versions = get_valid_versions; + get_valid_profile_names = get_valid_profile_names; + get_profile = get_profile; +}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/prosody-13.0.6/util-src/poll.c new/prosody-13.0.7/util-src/poll.c --- old/prosody-13.0.6/util-src/poll.c 2026-05-27 15:57:49.731381928 +0200 +++ new/prosody-13.0.7/util-src/poll.c 2026-09-23 18:44:22.389430863 +0200 @@ -234,6 +234,13 @@ #endif #ifdef USE_SELECT + if(fd >= FD_SETSIZE) { + luaL_pushfail(L); + lua_pushstring(L, strerror(EBADF)); + lua_pushinteger(L, EBADF); + return 3; + } + if(!FD_ISSET(fd, &state->all)) { luaL_pushfail(L); lua_pushstring(L, strerror(ENOENT)); @@ -327,6 +334,13 @@ #endif #ifdef USE_SELECT + if(fd >= FD_SETSIZE) { + luaL_pushfail(L); + lua_pushstring(L, strerror(EBADF)); + lua_pushinteger(L, EBADF); + return 3; + } + if(!FD_ISSET(fd, &state->all)) { luaL_pushfail(L); lua_pushstring(L, strerror(ENOENT));
