diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/mod_admin_shell.lua | 117 | ||||
-rw-r--r-- | plugins/mod_authz_internal.lua | 6 | ||||
-rw-r--r-- | plugins/mod_http.lua | 16 | ||||
-rw-r--r-- | plugins/mod_invites_register.lua | 16 | ||||
-rw-r--r-- | plugins/mod_s2s.lua | 24 | ||||
-rw-r--r-- | plugins/mod_storage_sql.lua | 71 | ||||
-rw-r--r-- | plugins/mod_tls.lua | 3 |
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"); |