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 "")); |
52 return reply; |
73 return reply; |
53 end |
74 end |
54 |
75 |
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 = session.sasl_handler:clean_clone(); |
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 module:get_option("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 if not (module:get_option("allow_unencrypted_plain_auth")) and not session.secure then |
|
145 session.sasl_handler:forbidden({"PLAIN"}); |
|
146 end |
|
147 end |
151 features:tag("mechanisms", mechanisms_attr); |
148 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. |
149 for k, v in pairs(session.sasl_handler:mechanisms()) do |
153 if config.get(session.host or "*", "core", "anonymous_login") then |
150 features:tag("mechanism"):text(v):up(); |
154 features:tag("mechanism"):text("ANONYMOUS"):up(); |
151 end |
155 else |
|
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(); |
152 features:up(); |
162 else |
153 else |
163 features:tag("bind", bind_attr):tag("required"):up():up(); |
154 features:tag("bind", bind_attr):tag("required"):up():up(); |
164 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); |
155 features:tag("session", xmpp_session_attr):tag("optional"):up():up(); |
165 end |
156 end |