aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/mod_admin_shell.lua117
-rw-r--r--plugins/mod_authz_internal.lua6
-rw-r--r--plugins/mod_http.lua16
-rw-r--r--plugins/mod_invites_register.lua16
-rw-r--r--plugins/mod_s2s.lua24
-rw-r--r--plugins/mod_storage_sql.lua71
-rw-r--r--plugins/mod_tls.lua3
7 files changed, 220 insertions, 33 deletions
diff --git a/plugins/mod_admin_shell.lua b/plugins/mod_admin_shell.lua
index c2b921b4..de345484 100644
--- a/plugins/mod_admin_shell.lua
+++ b/plugins/mod_admin_shell.lua
@@ -139,6 +139,8 @@ Built-in roles are:
prosody:admin - Host administrator
prosody:operator - Server administrator
+To view roles and policies, see the commands in 'help role'.
+
Roles can be assigned using the user management commands (see 'help user').
]];
@@ -2401,6 +2403,121 @@ function def_env.debug:async(runner_id)
return true, ("%d runners pending"):format(c);
end
+describe_command [[debug:cert_index([path]) - Show Prosody's view of a directory of certs]]
+function def_env.debug:cert_index(path)
+ local print = self.session.print;
+ local cm = require "core.certmanager";
+
+ path = path or module:get_option("certificates", "certs");
+
+ local sink = logger.add_simple_sink(function (source, level, message)
+ if source == "certmanager" then
+ if level == "debug" or level == "info" then
+ level = "II";
+ elseif level == "warn" or level == "error" then
+ level = "EE";
+ end
+ self.session.print(level..": "..message);
+ end
+ end);
+
+ print("II: Scanning "..path.."...");
+
+ local index = {};
+ cm.index_certs(path, index)
+
+ if not logger.remove_sink(sink) then
+ module:log("warn", "Unable to remove log sink");
+ end
+
+ local c, max_domain = 0, 8;
+ for domain in pairs(index) do
+ if #domain > max_domain then
+ max_domain = #domain;
+ end
+ end
+
+ print("");
+
+ local row = format_table({
+ { title = "Domain", width = max_domain };
+ { title = "Certificate", width = "100%" };
+ { title = "Service", width = 5 };
+ }, self.session.width);
+ print(row());
+ print(("-"):rep(self.session.width or 80));
+ for domain, certs in it.sorted_pairs(index) do
+ for cert_file, services in it.sorted_pairs(certs) do
+ for service in it.sorted_pairs(services) do
+ c = c + 1;
+ print(row({ domain, cert_file, service }));
+ end
+ end
+ end
+
+ print("");
+
+ return true, ("Showing %d certificates in %s"):format(c, path);
+end
+
+def_env.role = new_section("Role and access management");
+
+describe_command [[role:list(host) - List known roles]]
+function def_env.role:list(host)
+ if not host then
+ return nil, "Specify which host to list roles for";
+ end
+ local role_list = {};
+ for _, role in it.sorted_pairs(um.get_all_roles(host)) do
+ table.insert(role_list, role);
+ end
+ table.sort(role_list, function (a, b)
+ if a.priority ~= b.priority then
+ return (a.priority or 0) > (b.priority or 0);
+ end
+ return a.name < b.name;
+ end);
+ for _, role in ipairs(role_list) do
+ self.session.print(role.name);
+ end
+ return true, ("Showing %d roles on %s"):format(#role_list, host);
+end
+
+describe_command [[role:show(host, role_name) - Show information about a role]]
+function def_env.role:show(host, role_name)
+ if not host or not role_name then
+ return nil, "Specify the host and role to show";
+ end
+
+ local print = self.session.print;
+ local role = um.get_role_by_name(role_name, host);
+
+ if not role then
+ return nil, ("Unable to find role %s on host %s"):format(role_name, host);
+ end
+
+ local inherits = {};
+ for _, inherited_role in ipairs(role.inherits or {}) do
+ table.insert(inherits, inherited_role.name);
+ end
+
+ local permissions = {};
+ for permission, is_allowed in role:policies() do
+ permissions[permission] = is_allowed and "allowed" or "denied";
+ end
+
+ print("Name: ", role.name);
+ print("Inherits:", table.concat(inherits, ", "));
+ print("Policies:");
+ local c = 0;
+ for permission, policy in it.sorted_pairs(permissions) do
+ c = c + 1;
+ print(" ["..(policy == "allowed" and "+" or " ").."] " .. permission);
+ end
+ print("");
+ return true, ("Showing role %s with %d policies"):format(role.name, c);
+end
+
def_env.stats = new_section("Commands to show internal statistics");
local short_units = {
diff --git a/plugins/mod_authz_internal.lua b/plugins/mod_authz_internal.lua
index f683d90c..1282f617 100644
--- a/plugins/mod_authz_internal.lua
+++ b/plugins/mod_authz_internal.lua
@@ -298,7 +298,11 @@ function add_default_permission(role_name, action, policy)
end
function get_role_by_name(role_name)
- return assert(role_registry[role_name], role_name);
+ local role = role_registry[role_name];
+ if not role then
+ return error("Unknown role: "..role_name);
+ end
+ return role, role_name;
end
function get_all_roles()
diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index c13a2363..b5d0084c 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -331,7 +331,7 @@ local trusted_proxies = module:get_option_set("trusted_proxies", { "127.0.0.1",
--- deal with [ipv6]:port / ip:port format
local function normal_ip(ip)
- return ip:match("^%[([%x:]*)%]") or ip:match("^([%d.]+)") or ip;
+ return ip:match("^%[([%x:]*)%]") or ip:match("^%d+%.%d+%.%d+%.%d+") or ip;
end
local function is_trusted_proxy(ip)
@@ -339,7 +339,8 @@ local function is_trusted_proxy(ip)
if trusted_proxies[ip] then
return true;
end
- local parsed_ip = new_ip(ip)
+ local parsed_ip, err = new_ip(ip);
+ if not parsed_ip then return nil, err; end
for trusted_proxy in trusted_proxies do
if match_ip(parsed_ip, parse_cidr(trusted_proxy)) then
return true;
@@ -357,10 +358,14 @@ local function get_forwarded_connection_info(request) --> ip:string, secure:bool
request.forwarded = forwarded;
for i = #forwarded, 1, -1 do
local proxy = forwarded[i]
- if is_trusted_proxy(ip) then
+ local trusted, err = is_trusted_proxy(ip);
+ if trusted then
ip = normal_ip(proxy["for"]);
secure = secure and proxy.proto == "https";
else
+ if err then
+ request.log("warn", "Could not parse forwarded connection details: %s");
+ end
break
end
end
@@ -387,7 +392,10 @@ function get_forwarded_connection_info(request) --> ip:string, secure:boolean
-- Case d) If all IPs are in trusted proxies, something went obviously wrong and the logic never overwrites `ip`, leaving it at the original request IP.
forwarded_for = forwarded_for..", "..ip;
for forwarded_ip in forwarded_for:gmatch("[^%s,]+") do
- if not is_trusted_proxy(forwarded_ip) then
+ local trusted, err = is_trusted_proxy(forwarded_ip);
+ if err then
+ request.log("warn", "Could not parse forwarded connection details: %s");
+ elseif not trusted then
ip = forwarded_ip;
end
end
diff --git a/plugins/mod_invites_register.lua b/plugins/mod_invites_register.lua
index d9274ce4..76b644c7 100644
--- a/plugins/mod_invites_register.lua
+++ b/plugins/mod_invites_register.lua
@@ -101,8 +101,20 @@ module:hook("user-registering", function (event)
-- for this module to do...
return;
end
- if validated_invite and validated_invite.additional_data and validated_invite.additional_data.allow_reset then
- event.allow_reset = validated_invite.additional_data.allow_reset;
+ if validated_invite then
+ local username = validated_invite.username;
+ if username and username ~= event.username then
+ event.allowed = false;
+ event.reason = "The chosen username is not valid with this invitation";
+ end
+ local reset_username = validated_invite.additional_data and validated_invite.additional_data.allow_reset;
+ if reset_username then
+ if reset_username ~= event.username then
+ event.allowed = false;
+ event.reason = "Incorrect username for password reset";
+ end
+ event.allow_reset = reset_username;
+ end
end
end);
diff --git a/plugins/mod_s2s.lua b/plugins/mod_s2s.lua
index 7beab34a..5b81cf4f 100644
--- a/plugins/mod_s2s.lua
+++ b/plugins/mod_s2s.lua
@@ -995,16 +995,23 @@ end
-- Complete the sentence "Your certificate " with what's wrong
local function friendly_cert_error(session) --> string
if session.cert_chain_status == "invalid" then
+ local cert_errors = set.new();
+
if type(session.cert_chain_errors) == "table" then
- local cert_errors = set.new(session.cert_chain_errors[1]);
- if cert_errors:contains("certificate has expired") then
- return "has expired";
- elseif cert_errors:contains("self signed certificate") or cert_errors:contains("self-signed certificate") then
- return "is self-signed";
- elseif cert_errors:contains("no matching DANE TLSA records") then
- return "does not match any DANE TLSA records";
- end
+ cert_errors:add_list(session.cert_chain_errors[1]);
+ elseif type(session.cert_chain_errors) == "string" then
+ cert_errors:add(session.cert_chain_errors);
+ end
+ if cert_errors:contains("certificate has expired") then
+ return "has expired";
+ elseif cert_errors:contains("self signed certificate") or cert_errors:contains("self-signed certificate") then
+ return "is self-signed";
+ elseif cert_errors:contains("no matching DANE TLSA records") then
+ return "does not match any DANE TLSA records";
+ end
+
+ if type(session.cert_chain_errors) == "table" then
local chain_errors = set.new(session.cert_chain_errors[2]);
for i, e in pairs(session.cert_chain_errors) do
if i > 2 then chain_errors:add_list(e); end
@@ -1015,7 +1022,6 @@ local function friendly_cert_error(session) --> string
return "does not match any DANE TLSA records";
end
end
- -- TODO cert_chain_errors can be a string, handle that
return "is not trusted"; -- for some other reason
elseif session.cert_identity_status == "invalid" then
return "is not valid for this name";
diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua
index f053f729..6d9af68a 100644
--- a/plugins/mod_storage_sql.lua
+++ b/plugins/mod_storage_sql.lua
@@ -13,20 +13,24 @@ local t_concat = table.concat;
local have_dbisql, dbisql = pcall(require, "prosody.util.sql");
local have_sqlite, sqlite = pcall(require, "prosody.util.sqlite3");
-if not have_dbisql then
- module:log("debug", "Could not load LuaDBI: %s", dbisql)
- dbisql = nil;
-end
-if not have_sqlite then
- module:log("debug", "Could not load LuaSQLite3: %s", sqlite)
- sqlite = nil;
-end
if not (have_dbisql or have_sqlite) then
module:log("error", "LuaDBI or LuaSQLite3 are required for using SQL databases but neither are installed");
module:log("error", "Please install at least one of LuaDBI and LuaSQLite3. See https://prosody.im/doc/depends");
+ module:log("debug", "Could not load LuaDBI: %s", dbisql);
+ module:log("debug", "Could not load LuaSQLite3: %s", sqlite);
error("No SQL library available")
end
+local function get_sql_lib(driver)
+ if driver == "SQLite3" and have_sqlite then
+ return sqlite;
+ elseif have_dbisql then
+ return dbisql;
+ else
+ error(dbisql);
+ end
+end
+
local noop = function() end
local unpack = table.unpack;
local function iterator(result)
@@ -42,11 +46,11 @@ end
local function has_upsert(engine)
if engine.params.driver == "SQLite3" then
-- SQLite3 >= 3.24.0
- return engine.sqlite_version and (engine.sqlite_version[2] or 0) >= 24;
+ return engine.sqlite_version and (engine.sqlite_version[2] or 0) >= 24 and engine.has_upsert_index;
elseif engine.params.driver == "PostgreSQL" then
-- PostgreSQL >= 9.5
-- Versions without support have long since reached end of life.
- return true;
+ return engine.has_upsert_index;
end
-- We don't support UPSERT on MySQL/MariaDB, they seem to have a completely different syntax, uncertaint from which versions.
return false
@@ -757,7 +761,7 @@ end
local function create_table(engine) -- luacheck: ignore 431/engine
- local sql = engine.params.driver == "SQLite3" and sqlite or dbisql;
+ local sql = get_sql_lib(engine.params.driver);
local Table, Column, Index = sql.Table, sql.Column, sql.Index;
local ProsodyTable = Table {
@@ -798,7 +802,7 @@ end
local function upgrade_table(engine, params, apply_changes) -- luacheck: ignore 431/engine
local changes = false;
if params.driver == "MySQL" then
- local sql = dbisql;
+ local sql = get_sql_lib("MySQL");
local success,err = engine:transaction(function()
do
local result = assert(engine:execute("SHOW COLUMNS FROM \"prosody\" WHERE \"Field\"='value' and \"Type\"='text'"));
@@ -879,7 +883,7 @@ local function upgrade_table(engine, params, apply_changes) -- luacheck: ignore
indices[row[1]] = true;
end
elseif params.driver == "PostgreSQL" then
- for row in engine:select [[SELECT "indexname" FROM "pg_indexes" WHERE "tablename"='prosody' AND "indexname"='prosody_index';]] do
+ for row in engine:select [[SELECT "indexname" FROM "pg_indexes" WHERE "tablename"='prosody';]] do
indices[row[1]] = true;
end
end
@@ -893,6 +897,12 @@ local function upgrade_table(engine, params, apply_changes) -- luacheck: ignore
return false;
end
end
+ if not indices["prosody_unique_index"] then
+ module:log("warn", "Index \"prosody_unique_index\" does not exist, performance may be worse than normal!");
+ engine.has_upsert_index = false;
+ else
+ engine.has_upsert_index = true;
+ end
end
return changes;
end
@@ -920,7 +930,7 @@ end
function module.load()
local engines = module:shared("/*/sql/connections");
local params = normalize_params(module:get_option("sql", default_params));
- local sql = params.driver == "SQLite3" and sqlite or dbisql;
+ local sql = get_sql_lib(params.driver);
local db_uri = sql.db2uri(params);
engine = engines[db_uri];
if not engine then
@@ -1012,7 +1022,7 @@ function module.command(arg)
local uris = {};
for host in pairs(prosody.hosts) do -- luacheck: ignore 431/host
local params = normalize_params(config.get(host, "sql") or default_params);
- local sql = engine.params.driver == "SQLite3" and sqlite or dbisql;
+ local sql = get_sql_lib(engine.params.driver);
uris[sql.db2uri(params)] = params;
end
print("We will check and upgrade the following databases:\n");
@@ -1028,7 +1038,7 @@ function module.command(arg)
-- Upgrade each one
for _, params in pairs(uris) do
print("Checking "..params.database.."...");
- local sql = params.driver == "SQLite3" and sqlite or dbisql;
+ local sql = get_sql_lib(params.driver);
engine = sql:create_engine(params);
upgrade_table(engine, params, true);
end
@@ -1040,3 +1050,32 @@ function module.command(arg)
print("","upgrade - Perform database upgrade");
end
end
+
+module:add_item("shell-command", {
+ section = "sql";
+ section_desc = "SQL management commands";
+ name = "create";
+ desc = "Create the tables and indices used by Prosody (again)";
+ args = { { name = "host"; type = "string" } };
+ host_selector = "host";
+ handler = function(shell, _host)
+ local logger = require "prosody.util.logger";
+ local writing = false;
+ local sink = logger.add_simple_sink(function (source, _level, message)
+ local print = shell.session.print;
+ if writing or source ~= "sql" then return; end
+ writing = true;
+ print(message);
+ writing = false;
+ end);
+
+ local debug_enabled = engine._debug;
+ engine:debug(true);
+ create_table(engine);
+ engine:debug(debug_enabled);
+
+ if not logger.remove_sink(sink) then
+ module:log("warn", "Unable to remove log sink");
+ end
+ end;
+})
diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua
index ac215b81..a3af2f84 100644
--- a/plugins/mod_tls.lua
+++ b/plugins/mod_tls.lua
@@ -63,7 +63,8 @@ function module.load(reload)
module:log("debug", "Creating context for s2sout");
-- for outgoing server connections
- ssl_ctx_s2sout, err_s2sout, ssl_cfg_s2sout = create_context(host.host, "client", host_s2s, host_ssl, global_s2s, xmpp_alpn);
+ ssl_ctx_s2sout, err_s2sout, ssl_cfg_s2sout = create_context(host.host, "client", host_s2s, host_ssl, global_s2s, xmpp_alpn,
+ custom_cert_verification);
if not ssl_ctx_s2sout then module:log("error", "Error creating contexts for s2sout: %s", err_s2sout); end
module:log("debug", "Creating context for s2sin");