plugins/mod_saslauth.lua

changeset 2189
8fbbdb11a520
parent 2014
913c0845ef9a
parent 2175
c985536d5452
child 2200
de3edab7551d
equal deleted inserted replaced
2080:ca419b92a8c7 2189:8fbbdb11a520
1 -- Prosody IM 1 -- Prosody IM
2 -- Copyright (C) 2008-2009 Matthew Wild 2 -- Copyright (C) 2008-2009 Matthew Wild
3 -- Copyright (C) 2008-2009 Waqas Hussain 3 -- Copyright (C) 2008-2009 Waqas Hussain
4 -- 4 --
5 -- This project is MIT/X11 licensed. Please see the 5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information. 6 -- COPYING file in the source package for more information.
7 -- 7 --
8 8
9 9
33 local xmlns_bind ='urn:ietf:params:xml:ns:xmpp-bind'; 33 local xmlns_bind ='urn:ietf:params:xml:ns:xmpp-bind';
34 local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas'; 34 local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas';
35 35
36 local new_sasl = require "util.sasl".new; 36 local new_sasl = require "util.sasl".new;
37 37
38 default_authentication_profile = {
39 plain = function(username, realm)
40 local prepped_username = nodeprep(username);
41 if not prepped_username then
42 log("debug", "NODEprep failed on username: %s", username);
43 return "", nil;
44 end
45 local password = usermanager_get_password(prepped_username, realm);
46 if not password then
47 return "", nil;
48 end
49 return password, true;
50 end
51 };
52
53 anonymous_authentication_profile = {
54 anonymous = function(username, realm)
55 return true; -- for normal usage you should always return true here
56 end
57 }
58
38 local function build_reply(status, ret, err_msg) 59 local function build_reply(status, ret, err_msg)
39 local reply = st.stanza(status, {xmlns = xmlns_sasl}); 60 local reply = st.stanza(status, {xmlns = xmlns_sasl});
40 if status == "challenge" then 61 if status == "challenge" then
41 log("debug", "%s", ret or ""); 62 log("debug", "%s", ret or "");
42 reply:text(base64.encode(ret or "")); 63 reply:text(base64.encode(ret or ""));
55 local function handle_status(session, status) 76 local function handle_status(session, status)
56 if status == "failure" then 77 if status == "failure" then
57 session.sasl_handler = nil; 78 session.sasl_handler = nil;
58 elseif status == "success" then 79 elseif status == "success" then
59 local username = nodeprep(session.sasl_handler.username); 80 local username = nodeprep(session.sasl_handler.username);
60 session.sasl_handler = nil;
61 if not username then -- TODO move this to sessionmanager 81 if not username then -- TODO move this to sessionmanager
62 module:log("warn", "SASL succeeded but we didn't get a username!"); 82 module:log("warn", "SASL succeeded but we didn't get a username!");
63 session.sasl_handler = nil; 83 session.sasl_handler = nil;
64 session:reset_stream(); 84 session:reset_stream();
65 return; 85 return;
66 end 86 end
67 sm_make_authenticated(session, username); 87 sm_make_authenticated(session, session.sasl_handler.username);
88 session.sasl_handler = nil;
68 session:reset_stream(); 89 session:reset_stream();
69 end
70 end
71
72 local function credentials_callback(mechanism, ...)
73 if mechanism == "PLAIN" then
74 local username, hostname, password = ...;
75 username = nodeprep(username);
76 if not username then
77 return false;
78 end
79 local response = usermanager_validate_credentials(hostname, username, password, mechanism);
80 if response == nil then
81 return false;
82 else
83 return response;
84 end
85 elseif mechanism == "DIGEST-MD5" then
86 local function func(x) return x; end
87 local node, domain, realm, decoder = ...;
88 local prepped_node = nodeprep(node);
89 if not prepped_node then
90 return func, nil;
91 end
92 local password = usermanager_get_password(prepped_node, domain);
93 if password then
94 if decoder then
95 node, realm, password = decoder(node), decoder(realm), decoder(password);
96 end
97 return func, md5(node..":"..realm..":"..password);
98 else
99 return func, nil;
100 end
101 end 90 end
102 end 91 end
103 92
104 local function sasl_handler(session, stanza) 93 local function sasl_handler(session, stanza)
105 if stanza.name == "auth" then 94 if stanza.name == "auth" then
109 return session.send(build_reply("failure", "invalid-mechanism")); 98 return session.send(build_reply("failure", "invalid-mechanism"));
110 end 99 end
111 elseif stanza.attr.mechanism == "ANONYMOUS" then 100 elseif stanza.attr.mechanism == "ANONYMOUS" then
112 return session.send(build_reply("failure", "mechanism-too-weak")); 101 return session.send(build_reply("failure", "mechanism-too-weak"));
113 end 102 end
114 session.sasl_handler = new_sasl(stanza.attr.mechanism, session.host, credentials_callback); 103 local valid_mechanism = session.sasl_handler:select(stanza.attr.mechanism);
115 if not session.sasl_handler then 104 if not valid_mechanism then
116 return session.send(build_reply("failure", "invalid-mechanism")); 105 return session.send(build_reply("failure", "invalid-mechanism"));
117 end 106 end
118 elseif not session.sasl_handler then 107 elseif not session.sasl_handler then
119 return; -- FIXME ignoring out of order stanzas because ejabberd does 108 return; -- FIXME ignoring out of order stanzas because ejabberd does
120 end 109 end
126 session.sasl_handler = nil; 115 session.sasl_handler = nil;
127 session.send(build_reply("failure", "incorrect-encoding")); 116 session.send(build_reply("failure", "incorrect-encoding"));
128 return; 117 return;
129 end 118 end
130 end 119 end
131 local status, ret, err_msg = session.sasl_handler:feed(text); 120 local status, ret, err_msg = session.sasl_handler:process(text);
132 handle_status(session, status); 121 handle_status(session, status);
133 local s = build_reply(status, ret, err_msg); 122 local s = build_reply(status, ret, err_msg);
134 log("debug", "sasl reply: %s", tostring(s)); 123 log("debug", "sasl reply: %s", tostring(s));
135 session.send(s); 124 session.send(s);
136 end 125 end
146 function (session, features) 135 function (session, features)
147 if not session.username then 136 if not session.username then
148 if secure_auth_only and not session.secure then 137 if secure_auth_only and not session.secure then
149 return; 138 return;
150 end 139 end
140 if config.get(session.host or "*", "core", "anonymous_login") then
141 session.sasl_handler = new_sasl(session.host, anonymous_authentication_profile);
142 else
143 session.sasl_handler = new_sasl(session.host, default_authentication_profile);
144 end
151 features:tag("mechanisms", mechanisms_attr); 145 features:tag("mechanisms", mechanisms_attr);
152 -- TODO: Provide PLAIN only if TLS is active, this is a SHOULD from the introduction of RFC 4616. This behavior could be overridden via configuration but will issuing a warning or so. 146 -- TODO: Provide PLAIN only if TLS is active, this is a SHOULD from the introduction of RFC 4616. This behavior could be overridden via configuration but will issuing a warning or so.
153 if config.get(session.host or "*", "core", "anonymous_login") then 147 for k, v in pairs(session.sasl_handler:mechanisms()) do
154 features:tag("mechanism"):text("ANONYMOUS"):up(); 148 features:tag("mechanism"):text(v):up();
155 else 149 end
156 local mechanisms = usermanager_get_supported_methods(session.host or "*");
157 for k, v in pairs(mechanisms) do
158 features:tag("mechanism"):text(k):up();
159 end
160 end
161 features:up(); 150 features:up();
162 else 151 else
163 features:tag("bind", bind_attr):tag("required"):up():up(); 152 features:tag("bind", bind_attr):tag("required"):up():up();
164 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); 153 features:tag("session", xmpp_session_attr):tag("optional"):up():up();
165 end 154 end

mercurial