plugins/mod_legacyauth.lua

1
-- Prosody IM
2
-- Copyright (C) 2008-2010 Matthew Wild
3
-- Copyright (C) 2008-2010 Waqas Hussain
4
-- 
5
-- This project is MIT/X11 licensed. Please see the
6
-- COPYING file in the source package for more information.
7
--
8
 
9
 
10
 
11
local st = require "util.stanza";
12
local t_concat = table.concat;
13
 
14
local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption");
15
 
16
local sessionmanager = require "core.sessionmanager";
17
local usermanager = require "core.usermanager";
18
local nodeprep = require "util.encodings".stringprep.nodeprep;
19
local resourceprep = require "util.encodings".stringprep.resourceprep;
20
 
21
module:add_feature("jabber:iq:auth");
22
module:hook("stream-features", function(event)
23
	local origin, features = event.origin, event.features;
24
	if secure_auth_only and not origin.secure then
25
		-- Sorry, not offering to insecure streams!
26
		return;
27
	elseif not origin.username then
28
		features:tag("auth", {xmlns='http://jabber.org/features/iq-auth'}):up();
29
	end
30
end);
31
 
32
module:hook("stanza/iq/jabber:iq:auth:query", function(event)
33
	local session, stanza = event.origin, event.stanza;
34
 
35
	if session.type ~= "c2s_unauthed" then
36
		session.send(st.error_reply(stanza, "cancel", "service-unavailable", "Legacy authentication is only allowed for unauthenticated client connections."));
37
		return true;
38
	end
39
 
40
	if secure_auth_only and not session.secure then
41
		session.send(st.error_reply(stanza, "modify", "not-acceptable", "Encryption (SSL or TLS) is required to connect to this server"));
42
		return true;
43
	end
44
	
45
	local username = stanza.tags[1]:child_with_name("username");
46
	local password = stanza.tags[1]:child_with_name("password");
47
	local resource = stanza.tags[1]:child_with_name("resource");
48
	if not (username and password and resource) then
49
		local reply = st.reply(stanza);
50
		session.send(reply:query("jabber:iq:auth")
51
			:tag("username"):up()
52
			:tag("password"):up()
53
			:tag("resource"):up());
54
	else
55
		username, password, resource = t_concat(username), t_concat(password), t_concat(resource);
56
		username = nodeprep(username);
57
		resource = resourceprep(resource)
58
		local reply = st.reply(stanza);
59
		if usermanager.test_password(username, session.host, password) then
60
			-- Authentication successful!
61
			local success, err = sessionmanager.make_authenticated(session, username);
62
			if success then
63
				local err_type, err_msg;
64
				success, err_type, err, err_msg = sessionmanager.bind_resource(session, resource);
65
				if not success then
66
					session.send(st.error_reply(stanza, err_type, err, err_msg));
67
					session.username, session.type = nil, "c2s_unauthed"; -- FIXME should this be placed in sessionmanager?
68
					return true;
69
				elseif resource ~= session.resource then -- server changed resource, not supported by legacy auth
70
					session.send(st.error_reply(stanza, "cancel", "conflict", "The requested resource could not be assigned to this session."));
71
					session:close(); -- FIXME undo resource bind and auth instead of closing the session?
72
					return true;
73
				end
74
			end
75
			session.send(st.reply(stanza));
76
		else
77
			session.send(st.error_reply(stanza, "auth", "not-authorized"));
78
		end
79
	end
80
	return true;
81
end);