* src/ejabberd_c2s.erl: SASL support updated to xmpp-core-13

* src/cyrsasl*.erl: Support for authzid

SVN Revision: 114
This commit is contained in:
Alexey Shchepin 2003-06-07 17:30:25 +00:00
parent 2efb3e7e88
commit bc73337214
6 changed files with 176 additions and 129 deletions

View File

@ -1,3 +1,11 @@
2003-06-07 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_c2s.erl: SASL support updated to xmpp-core-13
2003-06-06 Alexey Shchepin <alexey@sevcom.net>
* src/cyrsasl*.erl: Support for authzid
2003-06-03 Alexey Shchepin <alexey@sevcom.net>
* src/msgs/fr.msg: New french translation (thanks to Vincent Ricard)

View File

@ -40,6 +40,30 @@ register_mechanism(Mechanism, Module) ->
ets:insert(sasl_mechanism, #sasl_mechanism{mechanism = Mechanism,
module = Module}).
% TODO: use callbacks
-include("ejabberd.hrl").
-include("jlib.hrl").
check_authzid(State, Props) ->
AuthzId = xml:get_attr_s(authzid, Props),
case jlib:string_to_jid(AuthzId) of
error ->
{error, "invalid-authzid"};
JID ->
LUser = xml:get_attr_s(username, Props),
{U, S, R} = jlib:jid_tolower(JID),
case R of
"" ->
{error, "invalid-authzid"};
_ ->
case {LUser, ?MYNAME} of
{U, S} ->
ok;
_ ->
{error, "invalid-authzid"}
end
end
end.
listmech() ->
ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1', _ = '_'}, [], ['$1']}]).
@ -58,7 +82,7 @@ server_start(State, Mech, ClientIn) ->
mech_state = MechState},
ClientIn);
_ ->
{error, "454"}
{error, "no-mechanism"}
end.
server_step(State, ClientIn) ->
@ -66,13 +90,16 @@ server_step(State, ClientIn) ->
MechState = State#sasl_state.mech_state,
case Module:mech_step(MechState, ClientIn) of
{ok, Props} ->
{ok, Props};
case check_authzid(State, Props) of
ok ->
{ok, Props};
{error, Error} ->
{error, Error}
end;
{continue, ServerOut, NewMechState} ->
{continue, ServerOut,
State#sasl_state{mech_state = NewMechState}};
{error, Code} ->
{error, Code}
{error, Error} ->
{error, Error}
end.

View File

@ -18,7 +18,7 @@
-behaviour(cyrsasl).
%-behaviour(gen_mod).
-record(state, {step, nonce, username}).
-record(state, {step, nonce, username, authzid}).
start(Opts) ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE),
@ -39,31 +39,38 @@ mech_step(#state{step = 1, nonce = Nonce} = State, "") ->
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
case parse(ClientIn) of
bad ->
{error, "454"};
{error, "bad-protocol"};
KeyVals ->
UserName = xml:get_attr_s("username", KeyVals),
AuthzId = xml:get_attr_s("authzid", KeyVals),
case ejabberd_auth:get_password(UserName) of
false ->
{error, "454"};
{error, "no-user"};
Passwd ->
Response = response(KeyVals, UserName, Passwd,
Response = response(KeyVals, UserName, Passwd, AuthzId,
"AUTHENTICATE"),
case xml:get_attr_s("response", KeyVals) of
Response ->
RspAuth = response(KeyVals, UserName, Passwd, ""),
RspAuth = response(KeyVals,
UserName, Passwd,
AuthzId, ""),
{continue,
"rspauth=" ++ RspAuth,
State#state{step = 5, username = UserName}};
State#state{step = 5,
username = UserName,
authzid = AuthzId}};
_ ->
{error, "454"}
{error, "bad-auth"}
end
end
end;
mech_step(#state{step = 5, username = UserName} = State, "") ->
{ok, [{username, UserName}]};
mech_step(#state{step = 5,
username = UserName,
authzid = AuthzId} = State, "") ->
{ok, [{username, UserName}, {authzid, AuthzId}]};
mech_step(A, B) ->
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
{error, "454"}.
{error, "bad-protocol"}.
parse(S) ->
@ -119,15 +126,23 @@ hex([N | Ns], Res) ->
digit_to_xchar(N div 16) | Res]).
response(KeyVals, User, Passwd, A2Prefix) ->
response(KeyVals, User, Passwd, AuthzId, A2Prefix) ->
Realm = xml:get_attr_s("realm", KeyVals),
Nonce = xml:get_attr_s("nonce", KeyVals),
CNonce = xml:get_attr_s("cnonce", KeyVals),
DigestURI = xml:get_attr_s("digest-uri", KeyVals),
NC = xml:get_attr_s("nc", KeyVals),
QOP = xml:get_attr_s("qop", KeyVals),
A1 = binary_to_list(crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
":" ++ Nonce ++ ":" ++ CNonce,
A1 = case AuthzId of
"" ->
binary_to_list(
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
":" ++ Nonce ++ ":" ++ CNonce;
_ ->
binary_to_list(
crypto:md5(User ++ ":" ++ Realm ++ ":" ++ Passwd)) ++
":" ++ Nonce ++ ":" ++ CNonce ++ ":" ++ AuthzId
end,
case QOP of
"auth" ->
A2 = A2Prefix ++ ":" ++ DigestURI;

View File

@ -27,15 +27,15 @@ mech_new() ->
mech_step(State, ClientIn) ->
case parse(ClientIn) of
[_, User, Password] ->
[AuthzId, User, Password] ->
case ejabberd_auth:check_password(User, Password) of
true ->
{ok, [{username, User}]};
{ok, [{username, User}, {authzid, AuthzId}]};
_ ->
{error, "454"}
{error, "bad-auth"}
end;
_ ->
{error, "454"}
{error, "bad-protocol"}
end.

View File

@ -20,7 +20,7 @@
wait_for_stream/2,
wait_for_auth/2,
wait_for_sasl_auth/2,
wait_for_resource_auth/2,
wait_for_session/2,
wait_for_sasl_response/2,
session_established/2,
handle_event/3,
@ -40,6 +40,7 @@
sasl_state,
access,
shaper,
authentificated = false,
user = "", server = ?MYNAME, resource = "",
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
@ -129,13 +130,18 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
{xmlelement, "mechanism", [],
[{xmlcdata, S}]}
end, cyrsasl:listmech()),
send_element(StateData,
{xmlelement, "stream:features", [],
[{xmlelement, "mechanisms",
[{"xmlns", ?NS_SASL_MECHANISMS}],
Mechs}]}),
{next_state, wait_for_sasl_auth,
StateData#state{sasl_state = SASLState}};
case StateData#state.authentificated of
false ->
send_element(StateData,
{xmlelement, "stream:features", [],
[{xmlelement, "mechanisms",
[{"xmlns", ?NS_SASL}],
Mechs}]}),
{next_state, wait_for_sasl_auth,
StateData#state{sasl_state = SASLState}};
_ ->
{next_state, wait_for_session, StateData}
end;
_ ->
Header = io_lib:format(
?STREAM_HEADER,
@ -245,32 +251,37 @@ wait_for_auth(closed, StateData) ->
wait_for_sasl_auth({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
case {xml:get_attr_s("xmlns", Attrs), Name} of
{?NS_SASL_MECHANISMS, "auth"} ->
{?NS_SASL, "auth"} ->
Mech = xml:get_attr_s("mechanism", Attrs),
ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
case cyrsasl:server_start(StateData#state.sasl_state,
Mech,
ClientIn) of
{ok, Props} ->
StateData#state.receiver ! reset_stream,
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL_MECHANISMS}], []}),
{next_state, wait_for_resource_auth,
StateData#state{user = xml:get_attr_s(username, Props)}};
[{"xmlns", ?NS_SASL}], []}),
{U, _, R} = jlib:string_to_jid(
xml:get_attr_s(authzid, Props)),
{next_state, wait_for_stream,
StateData#state{authentificated = true,
user = U,
resource = R
}};
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
{xmlelement, "challenge",
[{"xmlns", ?NS_SASL_MECHANISMS}],
[{"xmlns", ?NS_SASL}],
[{xmlcdata,
jlib:encode_base64(ServerOut)}]}),
{next_state, wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState}};
{error, Code} ->
{error, Error} ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_SASL_MECHANISMS},
{"code", Code}],
[]}),
[{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}),
{next_state, wait_for_sasl_auth, StateData}
end;
_ ->
@ -302,110 +313,38 @@ wait_for_sasl_auth(closed, StateData) ->
{stop, normal, StateData}.
wait_for_resource_auth({xmlstreamelement, El}, StateData) ->
case is_auth_packet(El) of
{auth, ID, get, {"", _, _, _}} ->
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
send_element(StateData, Err),
{next_state, wait_for_resource_auth, StateData};
{auth, ID, get, {U, _, _, _}} ->
{xmlelement, Name, Attrs, Els} = jlib:make_result_iq_reply(El),
Res = {xmlelement, Name, Attrs,
[{xmlelement, "query", [{"xmlns", ?NS_AUTH}],
[{xmlelement, "username", [],
[{xmlcdata, StateData#state.user}]},
{xmlelement, "resource", [], []}
]}]},
send_element(StateData, Res),
{next_state, wait_for_resource_auth, StateData};
{auth, ID, set, {U, _, _, ""}} ->
Err = jlib:make_error_reply(El, ?ERR_AUTH_NO_RESOURCE_PROVIDED),
send_element(StateData, Err),
{next_state, wait_for_resource_auth, StateData};
{auth, ID, set, {U, _, _, R}} ->
case StateData#state.user of
U ->
io:format("SASLAUTH: ~p~n", [{U, R}]),
JID = {U, ?MYNAME, R},
case acl:match_rule(StateData#state.access, JID) of
allow ->
ejabberd_sm:open_session(U, R),
Res = jlib:make_result_iq_reply(El),
send_element(StateData, Res),
change_shaper(StateData, JID),
{Fs, Ts} = mod_roster:get_subscription_lists(U),
{next_state, session_established,
StateData#state{user = U,
resource = R,
pres_f = ?SETS:from_list(Fs),
pres_t = ?SETS:from_list(Ts)}};
_ ->
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err),
{next_state, wait_for_resource_auth, StateData}
end;
_ ->
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
send_element(StateData, Err),
{next_state, wait_for_resource_auth, StateData}
end;
_ ->
case jlib:iq_query_info(El) of
{iq, ID, Type, ?NS_REGISTER, SubEl} ->
ResIQ = mod_register:process_iq(
{"", "", ""}, {"", ?MYNAME, ""},
{iq, ID, Type, ?NS_REGISTER, SubEl}),
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
{"", "", ""},
jlib:iq_to_xml(ResIQ)),
Res = jlib:remove_attr("to", Res1),
send_element(StateData, Res),
{next_state, wait_for_resource_auth, StateData};
_ ->
{next_state, wait_for_resource_auth, StateData}
end
end;
wait_for_resource_auth({xmlstreamend, Name}, StateData) ->
send_text(StateData, ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_resource_auth({xmlstreamerror, _}, StateData) ->
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_resource_auth(closed, StateData) ->
{stop, normal, StateData}.
% TODO: wait_for_sasl_response
wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
case {xml:get_attr_s("xmlns", Attrs), Name} of
{?NS_SASL_MECHANISMS, "response"} ->
{?NS_SASL, "response"} ->
ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
case cyrsasl:server_step(StateData#state.sasl_state,
ClientIn) of
{ok, Props} ->
StateData#state.receiver ! reset_stream,
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL_MECHANISMS}], []}),
{next_state, wait_for_resource_auth,
StateData#state{user = xml:get_attr_s(username, Props)}};
[{"xmlns", ?NS_SASL}], []}),
{U, _, R} = jlib:string_to_jid(
xml:get_attr_s(authzid, Props)),
{next_state, wait_for_stream,
StateData#state{authentificated = true,
user = U,
resource = R
}};
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
{xmlelement, "challenge",
[{"xmlns", ?NS_SASL_MECHANISMS}],
[{"xmlns", ?NS_SASL}],
[{xmlcdata,
jlib:encode_base64(ServerOut)}]}),
{next_state, wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState}};
{error, Code} ->
{error, Error} ->
send_element(StateData,
{xmlelement, "failure",
[{"xmlns", ?NS_SASL_MECHANISMS},
{"code", Code}],
[]}),
[{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}),
{next_state, wait_for_sasl_auth, StateData}
end;
_ ->
@ -438,6 +377,56 @@ wait_for_sasl_response(closed, StateData) ->
wait_for_session({xmlstreamelement, El}, StateData) ->
case jlib:iq_query_info(El) of
{iq, ID, set, ?NS_SESSION, SubEl} ->
U = StateData#state.user,
R = StateData#state.resource,
io:format("SASLAUTH: ~p~n", [{U, R}]),
JID = {U, ?MYNAME, R},
case acl:match_rule(StateData#state.access, JID) of
allow ->
ejabberd_sm:open_session(U, R),
Res = jlib:make_result_iq_reply(El),
send_element(StateData, Res),
change_shaper(StateData, JID),
{Fs, Ts} = mod_roster:get_subscription_lists(U),
{next_state, session_established,
StateData#state{pres_f = ?SETS:from_list(Fs),
pres_t = ?SETS:from_list(Ts)}};
_ ->
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err),
{next_state, wait_for_session, StateData}
end;
% TODO: is this needed?
{iq, ID, Type, ?NS_REGISTER, SubEl} ->
ResIQ = mod_register:process_iq(
{"", "", ""}, {"", ?MYNAME, ""},
{iq, ID, Type, ?NS_REGISTER, SubEl}),
Res1 = jlib:replace_from_to({"", ?MYNAME, ""},
{"", "", ""},
jlib:iq_to_xml(ResIQ)),
Res = jlib:remove_attr("to", Res1),
send_element(StateData, Res),
{next_state, wait_for_session, StateData};
_ ->
{next_state, wait_for_session, StateData}
end;
wait_for_session({xmlstreamend, Name}, StateData) ->
send_text(StateData, ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_session({xmlstreamerror, _}, StateData) ->
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_session(closed, StateData) ->
{stop, normal, StateData}.
session_established({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
@ -659,8 +648,15 @@ receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid, Timeout) ->
ShaperState
end,
NewShaperState = shaper:update(ShaperSt1, size(Text)),
xml_stream:send_text(XMLStreamPid, Text),
receiver(Socket, SockMod, NewShaperState, C2SPid, XMLStreamPid,
XMLStreamPid1 = receive
reset_stream ->
exit(XMLStreamPid, closed),
xml_stream:start(C2SPid)
after 0 ->
XMLStreamPid
end,
xml_stream:send_text(XMLStreamPid1, Text),
receiver(Socket, SockMod, NewShaperState, C2SPid, XMLStreamPid1,
Timeout);
{error, timeout} ->
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamPid,

View File

@ -32,7 +32,8 @@
-define(NS_STANZAS, "urn:ietf:params:xml:ns:xmpp-stanzas").
-define(NS_STREAMS, "urn:ietf:params:xml:ns:xmpp-streams").
-define(NS_SASL_MECHANISMS, "urn:ietf:params:xml:ns:xmpp-sasl").
-define(NS_SASL, "urn:ietf:params:xml:ns:xmpp-sasl").
-define(NS_SESSION, "urn:ietf:params:xml:ns:xmpp-session").
% TODO: remove "code" attribute (currently it used for backward-compatibility)
-define(STANZA_ERROR(Code, Class, Condition),