diff options
-rw-r--r-- | core/hostmanager.lua | 3 | ||||
-rw-r--r-- | core/s2smanager.lua | 2 | ||||
-rw-r--r-- | plugins/mod_admin_telnet.lua | 22 | ||||
-rw-r--r-- | plugins/mod_bosh.lua | 8 | ||||
-rw-r--r-- | plugins/mod_saslauth.lua | 12 | ||||
-rw-r--r-- | plugins/muc/mod_muc.lua | 8 | ||||
-rw-r--r-- | plugins/muc/muc.lib.lua | 34 | ||||
-rw-r--r-- | prosody.cfg.lua.dist | 2 | ||||
-rwxr-xr-x | prosodyctl | 101 | ||||
-rw-r--r-- | tools/ejabberdsql2prosody.lua | 7 | ||||
-rw-r--r-- | util/debug.lua | 58 | ||||
-rw-r--r-- | util/prosodyctl.lua | 21 | ||||
-rw-r--r-- | util/template.lua | 16 | ||||
-rw-r--r-- | util/x509.lua | 109 | ||||
-rw-r--r-- | util/xmppstream.lua | 33 |
15 files changed, 381 insertions, 55 deletions
diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 0dd1d426..330a0d03 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -12,6 +12,7 @@ local events_new = require "util.events".new; local disco_items = require "util.multitable".new(); local NULL = {}; +local jid_split = require "util.jid".split; local uuid_gen = require "util.uuid".generate; local log = require "util.logger".init("hostmanager"); @@ -23,7 +24,7 @@ if not _G.prosody.incoming_s2s then end local incoming_s2s = _G.prosody.incoming_s2s; -local pairs, setmetatable = pairs, setmetatable; +local pairs, setmetatable, select = pairs, setmetatable, select; local tostring, type = tostring, type; module "hostmanager" diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 158b5461..5907ff2f 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -99,7 +99,7 @@ function make_authenticated(session, host) else return false; end - session.log("debug", "connection %s->%s is now authenticated", session.from_host or "(unknown)", session.to_host or "(unknown)"); + session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host or "(unknown)", session.to_host or "(unknown)", host); mark_connected(session); diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index aa65b05b..202170ba 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -498,6 +498,24 @@ function def_env.c2s:close(match_jid) return true, "Total: "..count.." sessions closed"; end +local function session_flags(session, line) + if session.cert_identity_status == "valid" then + line[#line+1] = "(secure)"; + elseif session.secure then + line[#line+1] = "(encrypted)"; + end + if session.compressed then + line[#line+1] = "(compressed)"; + end + if session.smacks then + line[#line+1] = "(sm)"; + end + if session.conn and session.conn:ip():match(":") then + line[#line+1] = "(IPv6)"; + end + return table.concat(line, " "); +end + def_env.s2s = {}; function def_env.s2s:show(match_jid) local _print = self.session.print; @@ -510,7 +528,7 @@ function def_env.s2s:show(match_jid) for remotehost, session in pairs(host_session.s2sout) do if (not match_jid) or remotehost:match(match_jid) or host:match(match_jid) then count_out = count_out + 1; - print(" "..host.." -> "..remotehost..(session.cert_identity_status == "valid" and " (secure)" or "")..(session.secure and " (encrypted)" or "")..(session.compressed and " (compressed)" or "")); + print(session_flags(session, {" ", host, "->", remotehost})); if session.sendq then print(" There are "..#session.sendq.." queued outgoing stanzas for this connection"); end @@ -547,7 +565,7 @@ function def_env.s2s:show(match_jid) -- Pft! is what I say to list comprehensions or (session.hosts and #array.collect(keys(session.hosts)):filter(subhost_filter)>0)) then count_in = count_in + 1; - print(" "..host.." <- "..(session.from_host or "(unknown)")..(session.cert_identity_status == "valid" and " (secure)" or "")..(session.secure and " (encrypted)" or "")..(session.compressed and " (compressed)" or "")); + print(session_flags(session, {" ", host, "<-", session.from_host or "(unknown)"})); if session.type == "s2sin_unauthed" then print(" Connection not yet authenticated"); end diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 20a6fa1f..c5576004 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -123,10 +123,10 @@ function handle_request(method, body, request) -- stream:feed() calls the stream_callbacks, so all stanzas in -- the body are processed in this next line before it returns. - -- In particular, the streamopened() stream callback is where - -- much of the session logic happens, because it's where we first - -- get to see the 'sid' of this request. - stream:feed(body); + local ok, err = stream:feed(body); + if not ok then + log("error", "Failed to parse BOSH payload: %s", err); + end -- Stanzas (if any) in the request have now been processed, and -- we take care of the high-level BOSH logic here, including diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 9c62e5ec..7708572a 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -16,7 +16,6 @@ local base64 = require "util.encodings".base64; local cert_verify_identity = require "util.x509".verify_identity; -local nodeprep = require "util.encodings".stringprep.nodeprep; local usermanager_get_sasl_handler = require "core.usermanager".get_sasl_handler; local tostring = tostring; @@ -51,15 +50,14 @@ local function handle_status(session, status, ret, err_msg) module:fire_event("authentication-failure", { session = session, condition = ret, text = err_msg }); session.sasl_handler = session.sasl_handler:clean_clone(); elseif status == "success" then - module:fire_event("authentication-success", { session = session }); - local username = nodeprep(session.sasl_handler.username); - local ok, err = sm_make_authenticated(session, session.sasl_handler.username); if ok then + module:fire_event("authentication-success", { session = session }); session.sasl_handler = nil; session:reset_stream(); else module:log("warn", "SASL succeeded but username was invalid"); + module:fire_event("authentication-failure", { session = session, condition = "not-authorized", text = err }); session.sasl_handler = session.sasl_handler:clean_clone(); return "failure", "not-authorized", "User authenticated successfully, but username was invalid"; end @@ -191,8 +189,10 @@ local function s2s_external_auth(session, stanza) session.from_host = text; end session.sends2s(build_reply("success")) - module:log("info", "Accepting SASL EXTERNAL identity from %s", text or session.from_host); - s2s_make_authenticated(session, text or session.from_host) + + local domain = text ~= "" and text or session.from_host; + module:log("info", "Accepting SASL EXTERNAL identity from %s", domain); + s2s_make_authenticated(session, domain); session:reset_stream(); return true end diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 8ef7a5b3..43b8423d 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -69,10 +69,10 @@ for jid in pairs(persistent_rooms) do local node = jid_split(jid); local data = datamanager.load(node, muc_host, "config") or {}; local room = muc_new_room(jid, { - history_length = max_history_messages; + max_history_length = max_history_messages; }); room._data = data._data; - room._data.history_length = max_history_messages; --TODO: Need to allow per-room with a global limit + room._data.max_history_length = max_history_messages; -- Overwrite old max_history_length in data with current settings room._affiliations = data._affiliations; room.route_stanza = room_route_stanza; room.save = room_save; @@ -80,7 +80,7 @@ for jid in pairs(persistent_rooms) do end local host_room = muc_new_room(muc_host, { - history_length = max_history_messages; + max_history_length = max_history_messages; }); host_room.route_stanza = room_route_stanza; host_room.save = room_save; @@ -131,7 +131,7 @@ function stanza_handler(event) (restrict_room_creation == "admin" and is_admin(stanza.attr.from)) or (restrict_room_creation == "local" and select(2, jid_split(stanza.attr.from)) == module.host:gsub("^[^%.]+%.", "")) then room = muc_new_room(bare, { - history_length = max_history_messages; + max_history_length = max_history_messages; }); room.route_stanza = room_route_stanza; room.save = room_save; diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 731f9e37..286ad70c 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -138,7 +138,7 @@ function room_mt:broadcast_message(stanza, historic) stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) local entry = { stanza = stanza, stamp = stamp }; t_insert(history, entry); - while #history > (self._data.history_length or default_history_length) do t_remove(history, 1) end + while #history > self._data.history_length do t_remove(history, 1) end end end function room_mt:broadcast_except_nick(stanza, nick) @@ -339,6 +339,21 @@ end function room_mt:get_changesubject() return self._data.changesubject; end +function room_mt:get_historylength() + return self._data.history_length +end +function room_mt:set_historylength(length) + if tonumber(length) == nil then + return + end + length = tonumber(length); + log("debug", "max_history_length %s", self._data.max_history_length or "nil"); + if self._data.max_history_length and length > self._data.max_history_length then + length = self._data.max_history_length + end + self._data.history_length = length; +end + function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc local from, to = stanza.attr.from, stanza.attr.to; @@ -608,6 +623,12 @@ function room_mt:get_form_layout() type = 'boolean', label = 'Make Room Members-Only?', value = self:is_members_only() + }, + { + name = 'muc#roomconfig_historylength', + type = 'text-single', + label = 'Maximum Number of History Messages Returned by Room', + value = tostring(self:get_historylength()) } }); end @@ -659,6 +680,11 @@ function room_mt:process_form(origin, stanza) dirty = dirty or (self:get_changesubject() ~= (not changesubject and true or nil)) module:log('debug', 'changesubject=%s', changesubject and "true" or "false") + local historylength = fields['muc#roomconfig_historylength']; + dirty = dirty or (self:get_historylength() ~= (historylength and true or nil)) + module:log('debug', 'historylength=%s', historylength) + + local whois = fields['muc#roomconfig_whois']; if not valid_whois[whois] then origin.send(st.error_reply(stanza, 'cancel', 'bad-request', "Invalid value for 'whois'")); @@ -677,6 +703,7 @@ function room_mt:process_form(origin, stanza) self:set_persistent(persistent); self:set_hidden(not public); self:set_changesubject(changesubject); + self:set_historylength(historylength); if self.save then self:save(true); end origin.send(st.reply(stanza)); @@ -848,7 +875,7 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha origin.send(st.error_reply(stanza, "cancel", "forbidden")); end else - self:broadcast_message(stanza, true); + self:broadcast_message(stanza, self:get_historylength() > 0); end stanza.attr.from = from; end @@ -1102,7 +1129,8 @@ function _M.new_room(jid, config) _occupants = {}; _data = { whois = 'moderators'; - history_length = (config and config.history_length); + history_length = (config and config.max_history_length) or default_history_length; + max_history_length = (config and config.max_history_length) or default_history_length; }; _affiliations = {}; }, room_mt); diff --git a/prosody.cfg.lua.dist b/prosody.cfg.lua.dist index e513b116..db85a70b 100644 --- a/prosody.cfg.lua.dist +++ b/prosody.cfg.lua.dist @@ -45,7 +45,6 @@ modules_enabled = { --"compression"; -- Stream compression -- Nice to have - "legacyauth"; -- Legacy authentication. Only used by some old clients and bots. "version"; -- Replies to server version requests "uptime"; -- Report how long server has been running "time"; -- Let others know the time here on this server @@ -67,6 +66,7 @@ modules_enabled = { --"welcome"; -- Welcome users who register accounts --"watchregistrations"; -- Alert admins of registrations --"motd"; -- Send a message to users when they log in + --"legacyauth"; -- Legacy authentication. Only used by some old clients and bots. }; -- These modules are auto-loaded, should you @@ -236,6 +236,7 @@ local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warn local show_usage = prosodyctl.show_usage; local getchar, getpass = prosodyctl.getchar, prosodyctl.getpass; local show_yesno = prosodyctl.show_yesno; +local show_prompt = prosodyctl.show_prompt; local read_password = prosodyctl.read_password; local prosodyctl_timeout = (config.get("*", "core", "prosodyctl_timeout") or 5) * 2; @@ -612,6 +613,106 @@ function commands.unregister(arg) return 1; end +local x509 = require "util.x509"; +local genx509san = x509.genx509san; +local opensslbaseconf = x509.baseconf; +local seralizeopensslbaseconf = x509.serialize_conf; + +local cert_commands = {}; + +-- TODO Should this be moved to util.prosodyctl or x509? +function cert_commands.config(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local conf_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".cnf"; + if os.execute("test -f "..conf_filename) == 0 + and not show_yesno("Overwrite "..conf_filename .. "?") then + return nil, conf_filename; + end + local conf = opensslbaseconf(); + conf.subject_alternative_name = genx509san(hosts, config, arg, true) + for k, v in pairs(conf.distinguished_name) do + local nv; + if k == "commonName" then + v = arg[1] + elseif k == "emailAddress" then + v = "xmpp@" .. arg[1]; + end + nv = show_prompt(("%s (%s):"):format(k, nv or v)); + nv = (not nv or nv == "") and v or nv; + conf.distinguished_name[k] = nv ~= "." and nv or nil; + end + local conf_file = io.open(conf_filename, "w"); + conf_file:write(seralizeopensslbaseconf(conf)); + conf_file:close(); + print(""); + show_message("Config written to " .. conf_filename); + return nil, conf_filename; + else + show_usage("cert config HOSTNAME", "generates config for OpenSSL") + end +end + +function cert_commands.key(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local key_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".key"; + if os.execute("test -f "..key_filename) == 0 + and not show_yesno("Overwrite "..key_filename .. "?") then + return nil, key_filename; + end + local key_size = tonumber(arg[2] or show_prompt("Choose key size (2048):") or 2048); + os.execute(("openssl genrsa -out %s %d"):format(key_filename, tonumber(key_size))); + os.execute(("chmod 400 %s"):format(key_filename)); + show_message("Key written to ".. key_filename); + return nil, key_filename; + else + show_usage("cert key HOSTNAME <bits>", "Generates a RSA key") + end +end + +function cert_commands.request(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local req_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".req"; + if os.execute("test -f "..req_filename) == 0 + and not show_yesno("Overwrite "..req_filename .. "?") then + return nil, req_filename; + end + local _, key_filename = cert_commands.key({arg[1]}); + local _, conf_filename = cert_commands.config({arg[1]}); + os.execute(("openssl req -new -key %s -utf8 -config %s -out %s") + :format(key_filename, conf_filename, req_filename)); + show_message("Certificate request written to ".. req_filename); + else + show_usage("cert request HOSTNAME", "Generates a certificate request") + end +end + +function cert_commands.generate(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local cert_filename = (CFG_DATADIR or ".") .. "/" .. arg[1] .. ".cert"; + if os.execute("test -f "..cert_filename) == 0 + and not show_yesno("Overwrite "..cert_filename .. "?") then + return nil, cert_filename; + end + local _, key_filename = cert_commands.key({arg[1]}); + local _, conf_filename = cert_commands.config({arg[1]}); + os.execute(("openssl req -new -x509 -nodes -key %s -days 365 -sha1 -utf8 -config %s -out %s") + :format(key_filename, conf_filename, cert_filename)); + show_message("Certificate written to ".. cert_filename); + else + show_usage("cert generate HOSTNAME", "Generates a self-signed certificate") + end +end + +function commands.cert(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local subcmd = table.remove(arg, 1); + if type(cert_commands[subcmd]) == "function" then + return cert_commands[subcmd](arg); + end + end + show_usage("cert config|request|generate|key", "Helpers for X.509 certificates.") +end + --------------------- if command and command:match("^mod_") then -- Is a command in a module diff --git a/tools/ejabberdsql2prosody.lua b/tools/ejabberdsql2prosody.lua index 576f4436..c64faee0 100644 --- a/tools/ejabberdsql2prosody.lua +++ b/tools/ejabberdsql2prosody.lua @@ -129,7 +129,12 @@ local function readInsert() end end local tname = readTableName(); - for ch in ("` VALUES "):gmatch(".") do read(ch); end -- expect this + read("`"); read(" ") -- expect this + if peek() == "(" then -- skip column list + repeat until read() == ")"; + read(" "); + end + for ch in ("VALUES "):gmatch(".") do read(ch); end -- expect this local tuples = readTuples(); read(";"); read("\n"); return tname, tuples; diff --git a/util/debug.lua b/util/debug.lua index aeb710d7..7caf21ce 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -7,7 +7,21 @@ local censored_names = { pass = true; pwd = true; }; +local optimal_line_length = 65; +local termcolours = require "util.termcolours"; +local getstring = termcolours.getstring; +local styles; +do + _ = termcolours.getstyle; + styles = { + boundary_padding = _("bright", "white"); + filename = _("bright", "blue"); + level_num = _("green"); + funcname = _("yellow"); + location = _("yellow"); + }; +end module("debugx", package.seeall); function get_locals_table(level) @@ -90,30 +104,51 @@ function get_traceback_table(thread, start_level) return levels; end -function traceback(thread, message, level) +function traceback(...) + local ok, ret = pcall(_traceback, ...); + if not ok then + return "Error in error handling: "..ret; + end + return ret; +end + +local function build_source_boundary_marker(last_source_desc) + local padding = string.rep("-", math.floor(((optimal_line_length - 6) - #last_source_desc)/2)); + return getstring(styles.boundary_padding, "v"..padding).." "..getstring(styles.filename, last_source_desc).." "..getstring(styles.boundary_padding, padding..(#last_source_desc%2==0 and "-v" or "v ")); +end + +function _traceback(thread, message, level) + if type(thread) ~= "thread" then thread, message, level = coroutine.running(), thread, message; end if level and type(message) ~= "string" then return nil, "invalid message"; elseif not level then - level = message or 2; + if type(message) == "number" then + level, message = message, nil; + else + level = 2; + end end message = message and (message.."\n") or ""; local levels = get_traceback_table(thread, level+2); + local last_source_desc; + local lines = {}; for nlevel, level in ipairs(levels) do local info = level.info; local line = "..."; local func_type = info.namewhat.." "; + local source_desc = (info.short_src == "[C]" and "C code") or info.short_src or "Unknown"; if func_type == " " then func_type = ""; end; if info.short_src == "[C]" then - line = "[ C ] "..func_type.."C function "..(info.name and ("%q"):format(info.name) or "(unknown name)") + line = "[ C ] "..func_type.."C function "..getstring(styles.location, (info.name and ("%q"):format(info.name) or "(unknown name)")); elseif info.what == "main" then - line = "[Lua] "..info.short_src.." line "..info.currentline; + line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline); else local name = info.name or " "; if name ~= " " then @@ -122,20 +157,27 @@ function traceback(thread, message, level) if func_type == "global " or func_type == "local " then func_type = func_type.."function "; end - line = "[Lua] "..info.short_src.." line "..info.currentline.." in "..func_type..name.." defined on line "..info.linedefined; + line = "[Lua] "..getstring(styles.location, info.short_src.." line "..info.currentline).." in "..func_type..getstring(styles.funcname, name).." (defined on line "..info.linedefined..")"; + end + if source_desc ~= last_source_desc then -- Venturing into a new source, add marker for previous + last_source_desc = source_desc; + table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); end nlevel = nlevel-1; - table.insert(lines, "\t"..(nlevel==0 and ">" or " ").."("..nlevel..") "..line); + table.insert(lines, "\t"..(nlevel==0 and ">" or " ")..getstring(styles.level_num, "("..nlevel..") ")..line); local npadding = (" "):rep(#tostring(nlevel)); - local locals_str = string_from_var_table(level.locals, 65, "\t "..npadding); + local locals_str = string_from_var_table(level.locals, optimal_line_length, "\t "..npadding); if locals_str then table.insert(lines, "\t "..npadding.."Locals: "..locals_str); end - local upvalues_str = string_from_var_table(level.upvalues, 65, "\t "..npadding); + local upvalues_str = string_from_var_table(level.upvalues, optimal_line_length, "\t "..npadding); if upvalues_str then table.insert(lines, "\t "..npadding.."Upvals: "..upvalues_str); end end + +-- table.insert(lines, "\t "..build_source_boundary_marker(last_source_desc)); + return message.."stack traceback:\n"..table.concat(lines, "\n"); end diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index d0045abc..439de551 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -16,6 +16,7 @@ local signal = require "util.signal"; local set = require "util.set"; local lfs = require "lfs"; local pcall = pcall; +local type = type; local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; @@ -63,6 +64,13 @@ function getchar(n) end end +function getline() + local ok, line = pcall(io.read, "*l"); + if ok then + return line; + end +end + function getpass() local stty_ret = os.execute("stty -echo 2>/dev/null"); if stty_ret ~= 0 then @@ -112,6 +120,13 @@ function read_password() return password; end +function show_prompt(prompt) + io.write(prompt, " "); + local line = getline(); + line = line and line:gsub("\n$",""); + return (line and #line > 0) and line or nil; +end + -- Server control function adduser(params) local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; @@ -121,7 +136,11 @@ function adduser(params) return false, "invalid-hostname"; end - local provider = prosody.hosts[host].users; + local host_session = prosody.hosts[host]; + if not host_session then + return false, "no-such-host"; + end + local provider = host_session.users; if not(provider) or provider.name == "null" then usermanager.initialize_host(host); end diff --git a/util/template.lua b/util/template.lua index ebd8be14..5e9b479e 100644 --- a/util/template.lua +++ b/util/template.lua @@ -7,6 +7,7 @@ local ipairs = ipairs; local error = error; local loadstring = loadstring; local debug = debug; +local t_remove = table.remove; module("template") @@ -42,7 +43,6 @@ local parse_xml = (function() stanza:tag(name, attr); end function handler:CharacterData(data) - data = data:gsub("^%s*", ""):gsub("%s*$", ""); stanza:text(data); end function handler:EndElement(tagname) @@ -60,6 +60,19 @@ local parse_xml = (function() end; end)(); +local function trim_xml(stanza) + for i=#stanza,1,-1 do + local child = stanza[i]; + if child.name then + trim_xml(child); + else + child = child:gsub("^%s*", ""):gsub("%s*$", ""); + stanza[i] = child; + if child == "" then t_remove(stanza, i); end + end + end +end + local function create_string_string(str) str = ("%q"):format(str); str = str:gsub("{([^}]*)}", function(s) @@ -118,6 +131,7 @@ local template_mt = { __tostring = function(t) return t.name end }; local function create_template(templates, text) local stanza, err = parse_xml(text); if not stanza then error(err); end + trim_xml(stanza); local info = debug.getinfo(3, "Sl"); info = info and ("template(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.currentline) or "template(unknown)"; diff --git a/util/x509.lua b/util/x509.lua index d3c55bb4..f106e6fa 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -21,6 +21,10 @@ local nameprep = require "util.encodings".stringprep.nameprep; local idna_to_ascii = require "util.encodings".idna.to_ascii; local log = require "util.logger".init("x509"); +local pairs, ipairs = pairs, ipairs; +local s_format = string.format; +local t_insert = table.insert; +local t_concat = table.concat; module "x509" @@ -208,4 +212,109 @@ function verify_identity(host, service, cert) return false end +-- TODO Rename? Split out subroutines? +-- Also, this is probably openssl specific, what TODO about that? +function genx509san(hosts, config, certhosts, raw) -- recive config through that or some better way? + local function utf8string(s) + -- This is how we tell openssl not to encode UTF-8 strings as Latin1 + return s_format("FORMAT:UTF8,UTF8:%s", s); + end + + local function ia5string(s) + return s_format("IA5STRING:%s", s); + end + + local function dnsname(t, host) + t_insert(t.DNS, idna_to_ascii(host)); + end + + local function srvname(t, host, service) + t_insert(t.otherName, s_format("%s;%s", oid_dnssrv, ia5string("_" .. service .."." .. idna_to_ascii(host)))); + end + + local function xmppAddr(t, host) + t_insert(t.otherName, s_format("%s;%s", oid_xmppaddr, utf8string(host))); + end + + ----------------------------- + + local san = { + DNS = {}; + otherName = {}; + }; + + local sslsanconf = { }; + + for i = 1,#certhosts do + local certhost = certhosts[i]; + for name, host in pairs(hosts) do + if name == certhost or name:sub(-1-#certhost) == "."..certhost then + dnsname(san, name); + --print(name .. "#component_module: " .. (config.get(name, "core", "component_module") or "nil")); + if config.get(name, "core", "component_module") == nil then + srvname(san, name, "xmpp-client"); + end + --print(name .. "#anonymous_login: " .. tostring(config.get(name, "core", "anonymous_login"))); + if not (config.get(name, "core", "anonymous_login") or + config.get(name, "core", "authentication") == "anonymous") then + srvname(san, name, "xmpp-server"); + end + xmppAddr(san, name); + end + end + end + + for t, n in pairs(san) do + for i = 1,#n do + t_insert(sslsanconf, s_format("%s.%d = %s", t, i -1, n[i])); + end + end + + return raw and sslsanconf or t_concat(sslsanconf, "\n"); +end + +function baseconf() + return { + req = { + distinguished_name = "distinguished_name", + req_extensions = "v3_extensions", + x509_extensions = "v3_extensions", + prompt = "no", + }, + distinguished_name = { + commonName = "example.com", + countryName = "GB", + localityName = "The Internet", + organizationName = "Your Organisation", + organizationalUnitName = "XMPP Department", + emailAddress = "xmpp@example.com", + }, + v3_extensions = { + basicConstraints = "CA:FALSE", + keyUsage = "digitalSignature,keyEncipherment", + extendedKeyUsage = "serverAuth,clientAuth", + subjectAltName = "@subject_alternative_name", + }, + subject_alternative_name = { }, + } +end + +function serialize_conf(conf) + local s = ""; + for k, t in pairs(conf) do + s = s .. ("[%s]\n"):format(k); + if t[1] then + for i, v in ipairs(t) do + s = s .. ("%s\n"):format(v); + end + else + for k, v in pairs(t) do + s = s .. ("%s = %s\n"):format(k, v); + end + end + s = s .. "\n"; + end + return s; +end + return _M; diff --git a/util/xmppstream.lua b/util/xmppstream.lua index 0f80742d..f1793b4f 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -25,8 +25,11 @@ module "xmppstream" local new_parser = lxp.new; -local ns_prefixes = { - ["http://www.w3.org/XML/1998/namespace"] = "xml"; +local xml_namespace = { + ["http://www.w3.org/XML/1998/namespace\1lang"] = "xml:lang"; + ["http://www.w3.org/XML/1998/namespace\1space"] = "xml:space"; + ["http://www.w3.org/XML/1998/namespace\1base"] = "xml:base"; + ["http://www.w3.org/XML/1998/namespace\1id"] = "xml:id"; }; local xmlns_streams = "http://etherx.jabber.org/streams"; @@ -73,17 +76,13 @@ function new_sax_handlers(session, stream_callbacks) non_streamns_depth = non_streamns_depth + 1; end - -- FIXME !!!!! for i=1,#attr do local k = attr[i]; attr[i] = nil; - local ns, nm = k:match(ns_pattern); - if nm ~= "" then - ns = ns_prefixes[ns]; - if ns then - attr[ns..":"..nm] = attr[k]; - attr[k] = nil; - end + local xmlk = xml_namespace[k]; + if xmlk then + attr[xmlk] = attr[k]; + attr[k] = nil; end end @@ -140,19 +139,9 @@ function new_sax_handlers(session, stream_callbacks) stanza = t_remove(stack); end else - if tagname == stream_tag then - if cb_streamclosed then - cb_streamclosed(session); - end - else - local curr_ns,name = tagname:match(ns_pattern); - if name == "" then - curr_ns, name = "", curr_ns; - end - cb_error(session, "parse-error", "unexpected-element-close", name); + if cb_streamclosed then + cb_streamclosed(session); end - stanza, chardata = nil, {}; - stack = {}; end end |