/usr/lib/prosody/modules/mod_dialback.lua is in prosody 0.10.0-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | -- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local hosts = _G.hosts;
local log = module._log;
local st = require "util.stanza";
local sha256_hash = require "util.hashes".sha256;
local sha256_hmac = require "util.hashes".hmac_sha256;
local nameprep = require "util.encodings".stringprep.nameprep;
local check_cert_status = module:depends"s2s".check_cert_status;
local uuid_gen = require"util.uuid".generate;
local xmlns_stream = "http://etherx.jabber.org/streams";
local dialback_requests = setmetatable({}, { __mode = 'v' });
local dialback_secret = sha256_hash(module:get_option_string("dialback_secret", uuid_gen()), true);
local dwd = module:get_option_boolean("dialback_without_dialback", false);
function module.save()
return { dialback_secret = dialback_secret };
end
function module.restore(state)
dialback_secret = state.dialback_secret;
end
function generate_dialback(id, to, from)
return sha256_hmac(dialback_secret, to .. ' ' .. from .. ' ' .. id, true);
end
function initiate_dialback(session)
-- generate dialback key
session.dialback_key = generate_dialback(session.streamid, session.to_host, session.from_host);
session.sends2s(st.stanza("db:result", { from = session.from_host, to = session.to_host }):text(session.dialback_key));
session.log("debug", "sent dialback key on outgoing s2s stream");
end
function verify_dialback(id, to, from, key)
return key == generate_dialback(id, to, from);
end
module:hook("stanza/jabber:server:dialback:verify", function(event)
local origin, stanza = event.origin, event.stanza;
if origin.type == "s2sin_unauthed" or origin.type == "s2sin" then
-- We are being asked to verify the key, to ensure it was generated by us
origin.log("debug", "verifying that dialback key is ours...");
local attr = stanza.attr;
if attr.type then
module:log("warn", "Ignoring incoming session from %s claiming a dialback key for %s is %s",
origin.from_host or "(unknown)", attr.from or "(unknown)", attr.type);
return true;
end
-- COMPAT: Grr, ejabberd breaks this one too?? it is black and white in XEP-220 example 34
--if attr.from ~= origin.to_host then error("invalid-from"); end
local type;
if verify_dialback(attr.id, attr.from, attr.to, stanza[1]) then
type = "valid"
else
type = "invalid"
origin.log("warn", "Asked to verify a dialback key that was incorrect. An imposter is claiming to be %s?", attr.to);
end
origin.log("debug", "verified dialback key... it is %s", type);
origin.sends2s(st.stanza("db:verify", { from = attr.to, to = attr.from, id = attr.id, type = type }):text(stanza[1]));
return true;
end
end);
module:hook("stanza/jabber:server:dialback:result", function(event)
local origin, stanza = event.origin, event.stanza;
if origin.type == "s2sin_unauthed" or origin.type == "s2sin" then
-- he wants to be identified through dialback
-- We need to check the key with the Authoritative server
local attr = stanza.attr;
local to, from = nameprep(attr.to), nameprep(attr.from);
if not hosts[to] then
-- Not a host that we serve
origin.log("warn", "%s tried to connect to %s, which we don't serve", from, to);
origin:close("host-unknown");
return true;
elseif not from then
origin:close("improper-addressing");
end
if dwd and origin.secure then
if check_cert_status(origin, from) == false then
return
elseif origin.cert_chain_status == "valid" and origin.cert_identity_status == "valid" then
origin.sends2s(st.stanza("db:result", { to = from, from = to, id = attr.id, type = "valid" }));
module:fire_event("s2s-authenticated", { session = origin, host = from });
return true;
end
end
origin.hosts[from] = { dialback_key = stanza[1] };
dialback_requests[from.."/"..origin.streamid] = origin;
-- COMPAT: ejabberd, gmail and perhaps others do not always set 'to' and 'from'
-- on streams. We fill in the session's to/from here instead.
if not origin.from_host then
origin.from_host = from;
end
if not origin.to_host then
origin.to_host = to;
end
origin.log("debug", "asking %s if key %s belongs to them", from, stanza[1]);
module:fire_event("route/remote", {
from_host = to, to_host = from;
stanza = st.stanza("db:verify", { from = to, to = from, id = origin.streamid }):text(stanza[1]);
});
return true;
end
end);
module:hook("stanza/jabber:server:dialback:verify", function(event)
local origin, stanza = event.origin, event.stanza;
if origin.type == "s2sout_unauthed" or origin.type == "s2sout" then
local attr = stanza.attr;
local dialback_verifying = dialback_requests[attr.from.."/"..(attr.id or "")];
if dialback_verifying and attr.from == origin.to_host then
local valid;
if attr.type == "valid" then
module:fire_event("s2s-authenticated", { session = dialback_verifying, host = attr.from });
valid = "valid";
else
-- Warn the original connection that is was not verified successfully
log("warn", "authoritative server for %s denied the key", attr.from or "(unknown)");
valid = "invalid";
end
if dialback_verifying.destroyed then
log("warn", "Incoming s2s session %s was closed in the meantime, so we can't notify it of the db result", tostring(dialback_verifying):match("%w+$"));
else
dialback_verifying.sends2s(
st.stanza("db:result", { from = attr.to, to = attr.from, id = attr.id, type = valid })
:text(dialback_verifying.hosts[attr.from].dialback_key));
end
dialback_requests[attr.from.."/"..(attr.id or "")] = nil;
end
return true;
end
end);
module:hook("stanza/jabber:server:dialback:result", function(event)
local origin, stanza = event.origin, event.stanza;
if origin.type == "s2sout_unauthed" or origin.type == "s2sout" then
-- Remote server is telling us whether we passed dialback
local attr = stanza.attr;
if not hosts[attr.to] then
origin:close("host-unknown");
return true;
elseif hosts[attr.to].s2sout[attr.from] ~= origin then
-- This isn't right
origin:close("invalid-id");
return true;
end
if stanza.attr.type == "valid" then
module:fire_event("s2s-authenticated", { session = origin, host = attr.from });
else
origin:close("not-authorized", "dialback authentication failed");
end
return true;
end
end);
module:hook_stanza(xmlns_stream, "features", function (origin, stanza)
if not origin.external_auth or origin.external_auth == "failed" then
module:log("debug", "Initiating dialback...");
initiate_dialback(origin);
return true;
end
end, 100);
module:hook("s2sout-authenticate-legacy", function (event)
module:log("debug", "Initiating dialback...");
initiate_dialback(event.origin);
return true;
end, 100);
-- Offer dialback to incoming hosts
module:hook("s2s-stream-features", function (data)
data.features:tag("dialback", { xmlns='urn:xmpp:features:dialback' }):up();
end);
|