aboutsummaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/argparse.lua81
-rw-r--r--util/dnsregistry.lua3
-rw-r--r--util/prosodyctl.lua7
-rw-r--r--util/prosodyctl/check.lua345
-rw-r--r--util/prosodyctl/shell.lua26
-rw-r--r--util/sasl.lua11
-rw-r--r--util/sql.lua2
-rw-r--r--util/startup.lua31
-rw-r--r--util/x509.lua51
9 files changed, 455 insertions, 102 deletions
diff --git a/util/argparse.lua b/util/argparse.lua
index 7a55cb0b..75b1c2f9 100644
--- a/util/argparse.lua
+++ b/util/argparse.lua
@@ -2,6 +2,9 @@ local function parse(arg, config)
local short_params = config and config.short_params or {};
local value_params = config and config.value_params or {};
local array_params = config and config.array_params or {};
+ local kv_params = config and config.kv_params or {};
+ local strict = config and config.strict;
+ local stop_on_positional = not config or config.stop_on_positional ~= false;
local parsed_opts = {};
@@ -15,51 +18,69 @@ local function parse(arg, config)
end
local prefix = raw_param:match("^%-%-?");
- if not prefix then
+ if not prefix and stop_on_positional then
break;
elseif prefix == "--" and raw_param == "--" then
table.remove(arg, 1);
break;
end
- local param = table.remove(arg, 1):sub(#prefix+1);
- if #param == 1 and short_params then
- param = short_params[param];
- end
- if not param then
- return nil, "param-not-found", raw_param;
- end
+ if prefix then
+ local param = table.remove(arg, 1):sub(#prefix+1);
+ if #param == 1 and short_params then
+ param = short_params[param];
+ end
- local param_k, param_v;
- if value_params[param] or array_params[param] then
- param_k, param_v = param, table.remove(arg, 1);
- if not param_v then
- return nil, "missing-value", raw_param;
+ if not param then
+ return nil, "param-not-found", raw_param;
end
- else
- param_k, param_v = param:match("^([^=]+)=(.+)$");
- if not param_k then
- if param:match("^no%-") then
- param_k, param_v = param:sub(4), false;
- else
- param_k, param_v = param, true;
+
+ local uparam = param:match("^[^=]*"):gsub("%-", "_");
+
+ local param_k, param_v;
+ if value_params[uparam] or array_params[uparam] then
+ param_k = uparam;
+ param_v = param:match("^=(.*)$", #uparam+1);
+ if not param_v then
+ param_v = table.remove(arg, 1);
+ if not param_v then
+ return nil, "missing-value", raw_param;
+ end
+ end
+ else
+ param_k, param_v = param:match("^([^=]+)=(.+)$");
+ if not param_k then
+ if param:match("^no%-") then
+ param_k, param_v = param:sub(4), false;
+ else
+ param_k, param_v = param, true;
+ end
+ end
+ param_k = param_k:gsub("%-", "_");
+ if strict and not kv_params[param_k] then
+ return nil, "param-not-found", raw_param;
end
end
- param_k = param_k:gsub("%-", "_");
- end
- if array_params[param] then
- if parsed_opts[param_k] then
- table.insert(parsed_opts[param_k], param_v);
+ if array_params[uparam] then
+ if parsed_opts[param_k] then
+ table.insert(parsed_opts[param_k], param_v);
+ else
+ parsed_opts[param_k] = { param_v };
+ end
else
- parsed_opts[param_k] = { param_v };
+ parsed_opts[param_k] = param_v;
end
- else
- parsed_opts[param_k] = param_v;
+ elseif not stop_on_positional then
+ table.insert(parsed_opts, table.remove(arg, 1));
end
end
- for i = 1, #arg do
- parsed_opts[i] = arg[i];
+
+ if stop_on_positional then
+ for i = 1, #arg do
+ parsed_opts[i] = arg[i];
+ end
end
+
return parsed_opts;
end
diff --git a/util/dnsregistry.lua b/util/dnsregistry.lua
index b65debe0..56c76cac 100644
--- a/util/dnsregistry.lua
+++ b/util/dnsregistry.lua
@@ -1,5 +1,5 @@
-- Source: https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
--- Generated on 2024-10-26
+-- Generated on 2025-02-09
return {
classes = {
["IN"] = 1; [1] = "IN";
@@ -72,6 +72,7 @@ return {
["ZONEMD"] = 63; [63] = "ZONEMD";
["SVCB"] = 64; [64] = "SVCB";
["HTTPS"] = 65; [65] = "HTTPS";
+ ["DSYNC"] = 66; [66] = "DSYNC";
["SPF"] = 99; [99] = "SPF";
["NID"] = 104; [104] = "NID";
["L32"] = 105; [105] = "L32";
diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua
index 9cb4b4dd..c6856526 100644
--- a/util/prosodyctl.lua
+++ b/util/prosodyctl.lua
@@ -40,6 +40,7 @@ local error_messages = setmetatable({
["unable-to-save-data"] = "Unable to store, perhaps you don't have permission?";
["no-pidfile"] = "There is no 'pidfile' option in the configuration file, see https://prosody.im/doc/prosodyctl#pidfile for help";
["invalid-pidfile"] = "The 'pidfile' option in the configuration file is not a string, see https://prosody.im/doc/prosodyctl#pidfile for help";
+ ["pidfile-not-locked"] = "Stale pidfile found. Prosody is probably not running.";
["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info";
["no-such-method"] = "This module has no commands";
["not-running"] = "Prosody is not running";
@@ -143,8 +144,14 @@ local function getpid()
return false, "pidfile-read-failed", err;
end
+ -- Check for a lock on the file
local locked, err = lfs.lock(file, "w"); -- luacheck: ignore 211/err
if locked then
+ -- Prosody keeps the pidfile locked while it is running.
+ -- We successfully locked the file, which means Prosody is not
+ -- running and the pidfile is stale (somehow it was not
+ -- cleaned up). We'll abort here, to avoid sending signals to
+ -- a non-Prosody PID.
file:close();
return false, "pidfile-not-locked";
end
diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua
index ac8cc9c1..0a0ab878 100644
--- a/util/prosodyctl/check.lua
+++ b/util/prosodyctl/check.lua
@@ -11,6 +11,7 @@ local jid_split = require "prosody.util.jid".prepped_split;
local modulemanager = require "prosody.core.modulemanager";
local async = require "prosody.util.async";
local httputil = require "prosody.util.http";
+local human_units = require "prosody.util.human.units";
local function api(host)
return setmetatable({ name = "prosodyctl.check"; host = host; log = prosody.log }, { __index = moduleapi })
@@ -325,7 +326,12 @@ local function check(arg)
local ok = true;
local function contains_match(hayset, needle) for member in hayset do if member:find(needle) then return true end end end
local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end
- local function enabled_hosts() return it.filter(disabled_hosts, pairs(configmanager.getconfig())); end
+ local function is_user_host(host, conf) return host ~= "*" and conf.component_module == nil; end
+ local function is_component_host(host, conf) return host ~= "*" and conf.component_module ~= nil; end
+ local function enabled_hosts() return it.filter(disabled_hosts, it.sorted_pairs(configmanager.getconfig())); end
+ local function enabled_user_hosts() return it.filter(is_user_host, it.sorted_pairs(configmanager.getconfig())); end
+ local function enabled_components() return it.filter(is_component_host, it.sorted_pairs(configmanager.getconfig())); end
+
local checks = {};
function checks.disabled()
local disabled_hosts_set = set.new();
@@ -632,6 +638,12 @@ local function check(arg)
print(" Both mod_pep_simple and mod_pep are enabled but they conflict");
print(" with each other. Remove one.");
end
+ if all_modules:contains("posix") then
+ print("");
+ print(" mod_posix is loaded in your configuration file, but it has");
+ print(" been deprecated. You can safely remove it.");
+ end
+
for host, host_config in pairs(config) do --luacheck: ignore 213/host
if type(rawget(host_config, "storage")) == "string" and rawget(host_config, "default_storage") then
print("");
@@ -790,12 +802,28 @@ local function check(arg)
if #invalid_hosts > 0 or #alabel_hosts > 0 then
print("");
- print("WARNING: Changing the name of a VirtualHost in Prosody's config file");
- print(" WILL NOT migrate any existing data (user accounts, etc.) to the new name.");
+ print(" WARNING: Changing the name of a VirtualHost in Prosody's config file");
+ print(" WILL NOT migrate any existing data (user accounts, etc.) to the new name.");
ok = false;
end
end
+ -- Check features
+ do
+ local missing_features = {};
+ for host in enabled_user_hosts() do
+ local all_features = checks.features(host, true);
+ if not all_features then
+ table.insert(missing_features, host);
+ end
+ end
+ if #missing_features > 0 then
+ print("");
+ print(" Some of your hosts may be missing features due to a lack of configuration.");
+ print(" For more details, use the 'prosodyctl check features' command.");
+ end
+ end
+
print("Done.\n");
end
function checks.dns()
@@ -901,7 +929,11 @@ local function check(arg)
local unknown_addresses = set.new();
- for jid in enabled_hosts() do
+ local function is_valid_domain(domain)
+ return idna.to_ascii(domain) ~= nil;
+ end
+
+ for jid in it.filter(is_valid_domain, enabled_hosts()) do
local all_targets_ok, some_targets_ok = true, false;
local node, host = jid_split(jid);
@@ -1444,6 +1476,311 @@ local function check(arg)
end
end
end
+
+ function checks.features(check_host, quiet)
+ if not quiet then
+ print("Feature report");
+ end
+
+ local common_subdomains = {
+ http_file_share = "share";
+ muc = "groups";
+ };
+
+ local recommended_component_modules = {
+ muc = { "muc_mam" };
+ };
+
+ local function print_feature_status(feature, host)
+ if quiet then return; end
+ print("", feature.ok and "OK" or "(!)", feature.name);
+ if feature.desc then
+ print("", "", feature.desc);
+ print("");
+ end
+ if not feature.ok then
+ if feature.lacking_modules then
+ table.sort(feature.lacking_modules);
+ print("", "", "Suggested modules: ");
+ for _, module in ipairs(feature.lacking_modules) do
+ print("", "", (" - %s: https://prosody.im/doc/modules/mod_%s"):format(module, module));
+ end
+ end
+ if feature.lacking_components then
+ table.sort(feature.lacking_components);
+ for _, component_module in ipairs(feature.lacking_components) do
+ local subdomain = common_subdomains[component_module];
+ local recommended_mods = recommended_component_modules[component_module];
+ if subdomain then
+ print("", "", "Suggested component:");
+ print("");
+ print("", "", "", ("-- Documentation: https://prosody.im/doc/modules/mod_%s"):format(component_module));
+ print("", "", "", ("Component %q %q"):format(subdomain.."."..host, component_module));
+ if recommended_mods then
+ print("", "", "", " modules_enabled = {");
+ table.sort(recommended_mods);
+ for _, mod in ipairs(recommended_mods) do
+ print("", "", "", (" %q;"):format(mod));
+ end
+ print("", "", "", " }");
+ end
+ else
+ print("", "", ("Suggested component: %s"):format(component_module));
+ end
+ end
+ print("");
+ print("", "", "If you have already configured any of these components, they may not be");
+ print("", "", "linked correctly to "..host..". For more info see https://prosody.im/doc/components");
+ end
+ if feature.lacking_component_modules then
+ table.sort(feature.lacking_component_modules, function (a, b)
+ return a.host < b.host;
+ end);
+ for _, problem in ipairs(feature.lacking_component_modules) do
+ local hostapi = api(problem.host);
+ local current_modules_enabled = hostapi:get_option_array("modules_enabled", {});
+ print("", "", ("Component %q is missing the following modules: %s"):format(problem.host, table.concat(problem.missing_mods)));
+ print("");
+ print("","", "Add the missing modules to your modules_enabled under the Component, like this:");
+ print("");
+ print("");
+ print("", "", "", ("-- Documentation: https://prosody.im/doc/modules/mod_%s"):format(problem.component_module));
+ print("", "", "", ("Component %q %q"):format(problem.host, problem.component_module));
+ print("", "", "", (" modules_enabled = {"));
+ for _, mod in ipairs(current_modules_enabled) do
+ print("", "", "", (" %q;"):format(mod));
+ end
+ for _, mod in ipairs(problem.missing_mods) do
+ print("", "", "", (" %q; -- Add this!"):format(mod));
+ end
+ print("", "", "", (" }"));
+ end
+ end
+ end
+ if feature.meta then
+ for k, v in it.sorted_pairs(feature.meta) do
+ print("", "", (" - %s: %s"):format(k, v));
+ end
+ end
+ print("");
+ end
+
+ local all_ok = true;
+
+ local config = configmanager.getconfig();
+
+ local f, s, v;
+ if check_host then
+ f, s, v = it.values({ check_host });
+ else
+ f, s, v = enabled_user_hosts();
+ end
+
+ for host in f, s, v do
+ local modules_enabled = set.new(config["*"].modules_enabled);
+ modules_enabled:include(set.new(config[host].modules_enabled));
+
+ -- { [component_module] = { hostname1, hostname2, ... } }
+ local host_components = setmetatable({}, { __index = function (t, k) return rawset(t, k, {})[k]; end });
+
+ do
+ local hostapi = api(host);
+
+ -- Find implicitly linked components
+ for other_host in enabled_components() do
+ local parent_host = other_host:match("^[^.]+%.(.+)$");
+ if parent_host == host then
+ local component_module = configmanager.get(other_host, "component_module");
+ if component_module then
+ table.insert(host_components[component_module], other_host);
+ end
+ end
+ end
+
+ -- And components linked explicitly
+ for _, disco_item in ipairs(hostapi:get_option_array("disco_items", {})) do
+ local other_host = disco_item[1];
+ local component_module = configmanager.get(other_host, "component_module");
+ if component_module then
+ table.insert(host_components[component_module], other_host);
+ end
+ end
+ end
+
+ local current_feature;
+
+ local function check_module(suggested, alternate, ...)
+ if set.intersection(modules_enabled, set.new({suggested, alternate, ...})):empty() then
+ current_feature.lacking_modules = current_feature.lacking_modules or {};
+ table.insert(current_feature.lacking_modules, suggested);
+ end
+ end
+
+ local function check_component(suggested, alternate, ...)
+ local found;
+ for _, component_module in ipairs({ suggested, alternate, ... }) do
+ found = host_components[component_module][1];
+ if found then
+ local enabled_component_modules = api(found):get_option_inherited_set("modules_enabled");
+ local recommended_mods = recommended_component_modules[component_module];
+ if recommended_mods then
+ local missing_mods = {};
+ for _, mod in ipairs(recommended_mods) do
+ if not enabled_component_modules:contains(mod) then
+ table.insert(missing_mods, mod);
+ end
+ end
+ if #missing_mods > 0 then
+ if not current_feature.lacking_component_modules then
+ current_feature.lacking_component_modules = {};
+ end
+ table.insert(current_feature.lacking_component_modules, {
+ host = found;
+ component_module = component_module;
+ missing_mods = missing_mods;
+ });
+ end
+ end
+ break;
+ end
+ end
+ if not found then
+ current_feature.lacking_components = current_feature.lacking_components or {};
+ table.insert(current_feature.lacking_components, suggested);
+ end
+ return found;
+ end
+
+ local features = {
+ {
+ name = "Basic functionality";
+ desc = "Support for secure connections, authentication and messaging";
+ check = function ()
+ check_module("disco");
+ check_module("roster");
+ check_module("saslauth");
+ check_module("tls");
+ end;
+ };
+ {
+ name = "Multi-device messaging and data synchronization";
+ desc = "Multiple clients connected to the same account stay in sync";
+ check = function ()
+ check_module("carbons");
+ check_module("mam");
+ check_module("bookmarks");
+ check_module("pep");
+ end;
+ };
+ {
+ name = "Mobile optimizations";
+ desc = "Help mobile clients reduce battery and data usage";
+ check = function ()
+ check_module("smacks");
+ check_module("csi_simple", "csi_battery_saver");
+ end;
+ };
+ {
+ name = "Web connections";
+ desc = "Allow connections from browser-based web clients";
+ check = function ()
+ check_module("bosh");
+ check_module("websocket");
+ end;
+ };
+ {
+ name = "User profiles";
+ desc = "Enable users to publish profile information";
+ check = function ()
+ check_module("vcard_legacy", "vcard");
+ end;
+ };
+ {
+ name = "Blocking";
+ desc = "Block communication with chosen entities";
+ check = function ()
+ check_module("blocklist");
+ end;
+ };
+ {
+ name = "Push notifications";
+ desc = "Receive notifications on platforms that don't support persistent connections";
+ check = function ()
+ check_module("cloud_notify");
+ end;
+ };
+ {
+ name = "Audio/video calls and P2P";
+ desc = "Assist clients in setting up connections between each other";
+ check = function ()
+ check_module(
+ "turn_external",
+ "external_services",
+ "turncredentials",
+ "extdisco"
+ );
+ end;
+ };
+ {
+ name = "File sharing";
+ desc = "Sharing of files to groups and offline users";
+ check = function (self)
+ local service = check_component("http_file_share", "http_upload", "http_upload_external");
+ if service then
+ local size_limit;
+ if api(service):get_option("component_module") == "http_file_share" then
+ size_limit = api(service):get_option_number("http_file_share_size_limit", 10*1024*1024);
+ end
+ if size_limit then
+ self.meta = {
+ ["Size limit"] = human_units.format(size_limit, "b", "b");
+ };
+ end
+ end
+ end;
+ };
+ {
+ name = "Group chats";
+ desc = "Create group chats and channels";
+ check = function ()
+ check_component("muc");
+ end;
+ };
+ };
+
+ if not quiet then
+ print(host);
+ end
+
+ for _, feature in ipairs(features) do
+ current_feature = feature;
+ feature:check();
+ feature.ok = (
+ not feature.lacking_modules and
+ not feature.lacking_components and
+ not feature.lacking_component_modules
+ );
+ -- For improved presentation, we group the (ok) and (not ok) features
+ if feature.ok then
+ print_feature_status(feature, host);
+ end
+ end
+
+ for _, feature in ipairs(features) do
+ if not feature.ok then
+ all_ok = false;
+ print_feature_status(feature, host);
+ end
+ end
+
+ if not quiet then
+ print("");
+ end
+ end
+
+ return all_ok;
+ end
+
if what == nil or what == "all" then
local ret;
ret = checks.disabled();
diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua
index d6d885d8..31936989 100644
--- a/util/prosodyctl/shell.lua
+++ b/util/prosodyctl/shell.lua
@@ -29,8 +29,8 @@ local function read_line(prompt_string)
end
end
-local function send_line(client, line)
- client.send(st.stanza("repl-input", { width = tostring(term_width()) }):text(line));
+local function send_line(client, line, interactive)
+ client.send(st.stanza("repl-input", { width = tostring(term_width()), repl = interactive == false and "0" or "1" }):text(line));
end
local function repl(client)
@@ -64,6 +64,13 @@ local function printbanner()
print("https://prosody.im/doc/console\n");
end
+local function check()
+ local lfs = require "lfs";
+ local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock");
+ local state = lfs.attributes(socket_path, "mode");
+ return state == "socket";
+end
+
local function start(arg) --luacheck: ignore 212/arg
local client = adminstream.client();
local opts, err, where = parse_args(arg);
@@ -80,21 +87,11 @@ local function start(arg) --luacheck: ignore 212/arg
if arg[1] then
if arg[2] then
- local fmt = { "%s"; ":%s("; ")" };
- for i = 3, #arg do
- if arg[i]:sub(1, 1) == ":" then
- table.insert(fmt, i, ")%s(");
- elseif i > 3 and fmt[i - 1]:match("%%q$") then
- table.insert(fmt, i, ", %q");
- else
- table.insert(fmt, i, "%q");
- end
- end
- arg[1] = string.format(table.concat(fmt), table.unpack(arg));
+ arg[1] = ("{"..string.rep("%q", #arg, ", ").."}"):format(table.unpack(arg, 1, #arg));
end
client.events.add_handler("connected", function()
- send_line(client, arg[1]);
+ send_line(client, arg[1], false);
return true;
end, 1);
@@ -180,4 +177,5 @@ end
return {
shell = start;
+ available = check;
};
diff --git a/util/sasl.lua b/util/sasl.lua
index c3c22a1c..dc11d426 100644
--- a/util/sasl.lua
+++ b/util/sasl.lua
@@ -67,7 +67,7 @@ local function registerMechanism(name, backends, f, cb_backends)
end
-- create a new SASL object which can be used to authenticate clients
-local function new(realm, profile)
+local function new(realm, profile, userdata)
local mechanisms = profile.mechanisms;
if not mechanisms then
mechanisms = {};
@@ -80,7 +80,12 @@ local function new(realm, profile)
end
profile.mechanisms = mechanisms;
end
- return setmetatable({ profile = profile, realm = realm, mechs = mechanisms }, method);
+ return setmetatable({
+ profile = profile,
+ realm = realm,
+ mechs = mechanisms,
+ userdata = userdata
+ }, method);
end
-- add a channel binding handler
@@ -94,7 +99,7 @@ end
-- get a fresh clone with the same realm and profile
function method:clean_clone()
- return new(self.realm, self.profile)
+ return new(self.realm, self.profile, self.userdata)
end
-- get a list of possible SASL mechanisms to use
diff --git a/util/sql.lua b/util/sql.lua
index 2f0ec493..06550455 100644
--- a/util/sql.lua
+++ b/util/sql.lua
@@ -84,7 +84,7 @@ function engine:connect()
dbh:autocommit(false); -- don't commit automatically
self.conn = dbh;
self.prepared = {};
- if params.password then
+ if params.driver == "SQLite3" and params.password then
local ok, err = self:execute(("PRAGMA key='%s'"):format(dbh:quote(params.password)));
if not ok then
return ok, err;
diff --git a/util/startup.lua b/util/startup.lua
index c54fa56d..15f07fdf 100644
--- a/util/startup.lua
+++ b/util/startup.lua
@@ -89,6 +89,14 @@ function startup.read_config()
end
end
prosody.config_file = filename
+ local credentials_directory = os.getenv("CREDENTIALS_DIRECTORY");
+ if credentials_directory then
+ config.set_credentials_directory(credentials_directory);
+ elseif prosody.process_type == "prosody" then
+ config.set_credential_fallback_mode("error");
+ else
+ config.set_credential_fallback_mode("warn");
+ end
local ok, level, err = config.load(filename);
if not ok then
print("\n");
@@ -271,7 +279,6 @@ function startup.init_global_state()
config = CFG_CONFIGDIR or ".";
plugins = CFG_PLUGINDIR or "plugins";
data = "data";
- credentials = os.getenv("CREDENTIALS_DIRECTORY");
};
prosody.arg = _G.arg;
@@ -425,6 +432,19 @@ function startup.init_async()
async.set_schedule_function(timer.add_task);
end
+function startup.instrument()
+ local statsmanager = require "prosody.core.statsmanager";
+ local timed = require"prosody.util.openmetrics".timed;
+
+ local adns = require "prosody.net.adns";
+ if adns.instrument then
+ local m = statsmanager.metric("histogram", "prosody_dns", "seconds", "DNS lookups", { "qclass"; "qtype" }, {
+ buckets = { 1 / 1024; 1 / 256; 1 / 64; 1 / 16; 1 / 4; 1; 4 };
+ });
+ adns.instrument(function(qclass, qtype) return timed(m:with_labels(qclass, qtype)); end);
+ end
+end
+
function startup.init_data_store()
require "prosody.core.storagemanager";
end
@@ -814,12 +834,12 @@ function startup.hook_posix_signals()
end);
end
-function startup.systemd_notify()
+function startup.notification_socket()
local notify_socket_name = os.getenv("NOTIFY_SOCKET");
if not notify_socket_name then return end
local have_unix, unix = pcall(require, "socket.unix");
if not have_unix or type(unix) ~= "table" then
- log("error", "LuaSocket without UNIX socket support, can't notify systemd.")
+ log("error", "LuaSocket without UNIX socket support, can't notify process manager.")
return os.exit(1);
end
log("debug", "Will notify on socket %q", notify_socket_name);
@@ -827,7 +847,7 @@ function startup.systemd_notify()
local notify_socket = unix.dgram();
local ok, err = notify_socket:setpeername(notify_socket_name);
if not ok then
- log("error", "Could not connect to systemd notification socket %q: %q", notify_socket_name, err);
+ log("error", "Could not connect to notification socket %q: %q", notify_socket_name, err);
return os.exit(1);
end
local time = require "prosody.util.time";
@@ -922,13 +942,14 @@ function startup.prosody()
startup.load_secondary_libraries();
startup.init_promise();
startup.init_async();
+ startup.instrument();
startup.init_http_client();
startup.init_data_store();
startup.init_global_protection();
startup.posix_daemonize();
startup.write_pidfile();
startup.hook_posix_signals();
- startup.systemd_notify();
+ startup.notification_socket();
startup.prepare_to_start();
startup.notify_started();
end
diff --git a/util/x509.lua b/util/x509.lua
index 9ecb5b60..6d856be0 100644
--- a/util/x509.lua
+++ b/util/x509.lua
@@ -11,7 +11,8 @@
-- IDN libraries complicate that.
--- [TLS-CERTS] - https://www.rfc-editor.org/rfc/rfc6125.html
+-- [TLS-CERTS] - https://www.rfc-editor.org/rfc/rfc6125.html -- Obsolete
+-- [TLS-IDENT] - https://www.rfc-editor.org/rfc/rfc9525.html
-- [XMPP-CORE] - https://www.rfc-editor.org/rfc/rfc6120.html
-- [SRV-ID] - https://www.rfc-editor.org/rfc/rfc4985.html
-- [IDNA] - https://www.rfc-editor.org/rfc/rfc5890.html
@@ -35,10 +36,8 @@ local oid_subjectaltname = "2.5.29.17"; -- [PKIX] 4.2.1.6
local oid_xmppaddr = "1.3.6.1.5.5.7.8.5"; -- [XMPP-CORE]
local oid_dnssrv = "1.3.6.1.5.5.7.8.7"; -- [SRV-ID]
--- Compare a hostname (possibly international) with asserted names
--- extracted from a certificate.
--- This function follows the rules laid out in
--- sections 6.4.1 and 6.4.2 of [TLS-CERTS]
+-- Compare a hostname (possibly international) with asserted names extracted from a certificate.
+-- This function follows the rules laid out in section 6.3 of [TLS-IDENT]
--
-- A wildcard ("*") all by itself is allowed only as the left-most label
local function compare_dnsname(host, asserted_names)
@@ -159,61 +158,25 @@ local function verify_identity(host, service, cert)
if ext[oid_subjectaltname] then
local sans = ext[oid_subjectaltname];
- -- Per [TLS-CERTS] 6.3, 6.4.4, "a client MUST NOT seek a match for a
- -- reference identifier if the presented identifiers include a DNS-ID
- -- SRV-ID, URI-ID, or any application-specific identifier types"
- local had_supported_altnames = false
-
if sans[oid_xmppaddr] then
- had_supported_altnames = true
if service == "_xmpp-client" or service == "_xmpp-server" then
if compare_xmppaddr(host, sans[oid_xmppaddr]) then return true end
end
end
if sans[oid_dnssrv] then
- had_supported_altnames = true
-- Only check srvNames if the caller specified a service
if service and compare_srvname(host, service, sans[oid_dnssrv]) then return true end
end
if sans["dNSName"] then
- had_supported_altnames = true
if compare_dnsname(host, sans["dNSName"]) then return true end
end
-
- -- We don't need URIs, but [TLS-CERTS] is clear.
- if sans["uniformResourceIdentifier"] then
- had_supported_altnames = true
- end
-
- if had_supported_altnames then return false end
- end
-
- -- Extract a common name from the certificate, and check it as if it were
- -- a dNSName subjectAltName (wildcards may apply for, and receive,
- -- cat treats)
- --
- -- Per [TLS-CERTS] 1.8, a CN-ID is the Common Name from a cert subject
- -- which has one and only one Common Name
- local subject = cert:subject()
- local cn = nil
- for i=1,#subject do
- local dn = subject[i]
- if dn["oid"] == oid_commonname then
- if cn then
- log("info", "Certificate has multiple common names")
- return false
- end
-
- cn = dn["value"];
- end
end
- if cn then
- -- Per [TLS-CERTS] 6.4.4, follow the comparison rules for dNSName SANs.
- return compare_dnsname(host, { cn })
- end
+ -- Per [TLS-IDENT] ignore the Common Name
+ -- The server identity can only be expressed in the subjectAltNames extension;
+ -- it is no longer valid to use the commonName RDN, known as CN-ID in [TLS-CERTS].
-- If all else fails, well, why should we be any different?
return false