* src/ejabberd_s2s_out.erl: Fixed ports leak

* src/ejabberd_listener.erl: Likewise

* src/ejabberd_c2s.erl: Fixes for SASL support

* src/cyrsasl.erl: Fixes

* src/cyrsasl_digest.erl: DIGEST-MD5 SASL mechanism support

SVN Revision: 87
This commit is contained in:
Alexey Shchepin 2003-03-12 19:48:05 +00:00
parent f3916bddd2
commit 8b26ac9e97
8 changed files with 235 additions and 22 deletions

View File

@ -1,3 +1,14 @@
2003-03-12 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_s2s_out.erl: Fixed ports leak
* src/ejabberd_listener.erl: Likewise
* src/ejabberd_c2s.erl: Fixes for SASL support
* src/cyrsasl.erl: Fixes
* src/cyrsasl_digest.erl: DIGEST-MD5 SASL mechanism support
2003-03-09 Alexey Shchepin <alexey@sevcom.net>
* src/cyrsasl*.erl: SASL support (currently support only PLAIN

View File

@ -33,6 +33,7 @@ start() ->
public,
{keypos, #sasl_mechanism.mechanism}]),
cyrsasl_plain:start([]),
cyrsasl_digest:start([]),
ok.
register_mechanism(Mechanism, Module) ->
@ -52,7 +53,7 @@ server_new(Service, ServerFQDN, UserRealm, SecFlags) ->
server_start(State, Mech, ClientIn) ->
case ets:lookup(sasl_mechanism, Mech) of
[#sasl_mechanism{module = Module}] ->
MechState = Module:mech_new(),
{ok, MechState} = Module:mech_new(),
server_step(State#sasl_state{mech_mod = Module,
mech_state = MechState},
ClientIn);

144
src/cyrsasl_digest.erl Normal file
View File

@ -0,0 +1,144 @@
%%%----------------------------------------------------------------------
%%% File : cyrsasl_digest.erl
%%% Author : Alexey Shchepin <alexey@sevcom.net>
%%% Purpose : DIGEST-MD5 SASL mechanism
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% Id : $Id$
%%%----------------------------------------------------------------------
-module(cyrsasl_digest).
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
-export([start/1,
stop/0,
mech_new/0,
mech_step/2]).
-behaviour(cyrsasl).
%-behaviour(gen_mod).
-record(state, {step, nonce, username}).
start(Opts) ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE),
ok.
stop() ->
ok.
mech_new() ->
{ok, #state{step = 1,
nonce = randoms:get_string()}}.
mech_step(#state{step = 1, nonce = Nonce} = State, "") ->
{continue,
"nonce=\"" ++ jlib:encode_base64(Nonce) ++
"\",qop=\"auth,auth-int\",charset=utf-8,algorithm=md5-sess",
State#state{step = 3}};
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
case parse(ClientIn) of
bad ->
{error, "454"};
KeyVals ->
UserName = xml:get_attr_s("username", KeyVals),
case ejabberd_auth:get_password(UserName) of
false ->
{error, "454"};
Passwd ->
Response = response(KeyVals, UserName, Passwd,
"AUTHENTICATE"),
case xml:get_attr_s("response", KeyVals) of
Response ->
RspAuth = response(KeyVals, UserName, Passwd, ""),
{continue,
"rspauth=" ++ RspAuth,
State#state{step = 5, username = UserName}};
_ ->
{error, "454"}
end
end
end;
mech_step(#state{step = 5, username = UserName} = State, "") ->
{ok, [{username, UserName}]};
mech_step(A, B) ->
io:format("SASL DIGEST: A ~p B ~p", [A,B]),
{error, "454"}.
parse(S) ->
parse1(S, "", []).
parse1([$= | Cs], S, Ts) ->
parse2(Cs, lists:reverse(S), "", Ts);
parse1([C | Cs], S, Ts) ->
parse1(Cs, [C | S], Ts);
parse1([], [], T) ->
lists:reverse(T);
parse1([], S, T) ->
bad.
parse2([$" | Cs], Key, Val, Ts) ->
parse3(Cs, Key, Val, Ts);
parse2([C | Cs], Key, Val, Ts) ->
parse4(Cs, Key, [C | Val], Ts);
parse2([], _, _, _) ->
bad.
parse3([$" | Cs], Key, Val, Ts) ->
parse4(Cs, Key, Val, Ts);
parse3([C | Cs], Key, Val, Ts) ->
parse3(Cs, Key, [C | Val], Ts);
parse3([], _, _, _) ->
bad.
parse4([$, | Cs], Key, Val, Ts) ->
parse1(Cs, "", [{Key, lists:reverse(Val)} | Ts]);
parse4([C | Cs], Key, Val, Ts) ->
parse4(Cs, Key, [C | Val], Ts);
parse4([], Key, Val, Ts) ->
parse1([], "", [{Key, lists:reverse(Val)} | Ts]).
digit_to_xchar(D) when (D >= 0) and (D < 10) ->
D + 48;
digit_to_xchar(D) ->
D + 87.
hex(S) ->
hex(S, []).
hex([], Res) ->
lists:reverse(Res);
hex([N | Ns], Res) ->
hex(Ns, [digit_to_xchar(N rem 16),
digit_to_xchar(N div 16) | Res]).
response(KeyVals, User, Passwd, 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,
case QOP of
"auth" ->
A2 = A2Prefix ++ ":" ++ DigestURI;
_ ->
A2 = A2Prefix ++ ":" ++ DigestURI ++
":00000000000000000000000000000000"
end,
T = hex(binary_to_list(crypto:md5(A1))) ++ ":" ++ Nonce ++ ":" ++
NC ++ ":" ++ CNonce ++ ":" ++ QOP ++ ":" ++
hex(binary_to_list(crypto:md5(A2))),
hex(binary_to_list(crypto:md5(T))).

View File

@ -19,6 +19,7 @@
check_password/4,
try_register/2,
dirty_get_registered_users/0,
get_password/1,
get_password_s/1,
is_user_exists/1,
remove_user/1,
@ -166,6 +167,15 @@ try_register(User, Password) ->
dirty_get_registered_users() ->
mnesia:dirty_all_keys(passwd).
get_password(User) ->
LUser = jlib:tolower(User),
case catch mnesia:dirty_read(passwd, LUser) of
[#passwd{password = Password}] ->
Password;
_ ->
false
end.
get_password_s(User) ->
LUser = jlib:tolower(User),
case catch mnesia:dirty_read(passwd, LUser) of

View File

@ -21,6 +21,7 @@
wait_for_auth/2,
wait_for_sasl_auth/2,
wait_for_resource_auth/2,
wait_for_sasl_response/2,
session_established/2,
handle_event/3,
handle_sync_event/4,
@ -258,7 +259,8 @@ wait_for_sasl_auth({xmlstreamelement, El}, StateData) ->
[{"xmlns", ?NS_SASL_MECHANISMS}],
[{xmlcdata,
jlib:encode_base64(ServerOut)}]}),
{next_state, wait_for_sasl_response, StateData};
{next_state, wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState}};
{error, Code} ->
send_element(StateData#state.socket,
{xmlelement, "failure",
@ -373,6 +375,63 @@ wait_for_resource_auth(closed, 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"} ->
ClientIn = jlib:decode_base64(xml:get_cdata(Els)),
case cyrsasl:server_step(StateData#state.sasl_state,
ClientIn) of
{ok, Props} ->
send_element(StateData#state.socket,
{xmlelement, "success",
[{"xmlns", ?NS_SASL_MECHANISMS}], []}),
{next_state, wait_for_resource_auth,
StateData#state{user = xml:get_attr_s(username, Props)}};
{continue, ServerOut, NewSASLState} ->
send_element(StateData#state.socket,
{xmlelement, "challenge",
[{"xmlns", ?NS_SASL_MECHANISMS}],
[{xmlcdata,
jlib:encode_base64(ServerOut)}]}),
{next_state, wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState}};
{error, Code} ->
send_element(StateData#state.socket,
{xmlelement, "failure",
[{"xmlns", ?NS_SASL_MECHANISMS},
{"code", Code}],
[]}),
{next_state, wait_for_sasl_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#state.socket, Res),
{next_state, wait_for_sasl_auth, StateData};
_ ->
{next_state, wait_for_sasl_auth, StateData}
end
end;
wait_for_sasl_response({xmlstreamend, Name}, StateData) ->
send_text(StateData#state.socket, ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_sasl_response({xmlstreamerror, _}, StateData) ->
send_text(StateData#state.socket, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
{stop, normal, StateData};
wait_for_sasl_response(closed, StateData) ->
{stop, normal, StateData}.

View File

@ -55,8 +55,9 @@ init(Port, Module, Fun, Opts) ->
accept(ListenSocket, Module, Fun, Opts) ->
case gen_tcp:accept(ListenSocket) of
{ok,Socket} ->
apply(Module, Fun, [{gen_tcp, Socket}, Opts]),
{ok, Socket} ->
{ok, Pid} = apply(Module, Fun, [{gen_tcp, Socket}, Opts]),
gen_tcp:controlling_process(Socket, Pid),
accept(ListenSocket, Module, Fun, Opts)
end.
@ -73,7 +74,7 @@ init_ssl(Port, Module, Fun, Opts, SSLOpts) ->
accept_ssl(ListenSocket, Module, Fun, Opts) ->
case ssl:accept(ListenSocket) of
{ok,Socket} ->
{ok, Socket} ->
apply(Module, Fun, [{ssl, Socket}, Opts]),
accept_ssl(ListenSocket, Module, Fun, Opts)
end.

View File

@ -13,7 +13,7 @@
-behaviour(gen_fsm).
%% External exports
-export([start/3, receiver/2, send_text/2, send_element/2]).
-export([start/3, send_text/2, send_element/2]).
%% gen_fsm callbacks
-export([init/1,
@ -350,7 +350,8 @@ terminate(Reason, StateName, StateData) ->
undefined ->
ok;
Socket ->
gen_tcp:close(Socket)
gen_tcp:close(Socket),
exit(StateData#state.xmlpid, closed)
end,
ok.
@ -358,21 +359,6 @@ terminate(Reason, StateName, StateData) ->
%%% Internal functions
%%%----------------------------------------------------------------------
receiver(Socket, C2SPid) ->
XMLStreamPid = xml_stream:start(C2SPid),
receiver(Socket, C2SPid, XMLStreamPid).
receiver(Socket, C2SPid, XMLStreamPid) ->
case gen_tcp:recv(Socket, 0) of
{ok, Text} ->
xml_stream:send_text(XMLStreamPid, Text),
receiver(Socket, C2SPid, XMLStreamPid);
{error, Reason} ->
exit(XMLStreamPid, closed),
gen_fsm:send_event(C2SPid, closed),
ok
end.
send_text(Socket, Text) ->
gen_tcp:send(Socket,Text).

View File

@ -16,6 +16,7 @@ start(CallbackPid) ->
spawn(?MODULE, init, [CallbackPid]).
init(CallbackPid) ->
link(CallbackPid),
Port = open_port({spawn, expat_erl}, [binary]),
loop(CallbackPid, Port, []).