From 8b26ac9e970e5256ec278a0f38656d93e53070c5 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 12 Mar 2003 19:48:05 +0000 Subject: [PATCH] * 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 --- ChangeLog | 11 +++ src/cyrsasl.erl | 3 +- src/cyrsasl_digest.erl | 144 ++++++++++++++++++++++++++++++++++++++ src/ejabberd_auth.erl | 10 +++ src/ejabberd_c2s.erl | 61 +++++++++++++++- src/ejabberd_listener.erl | 7 +- src/ejabberd_s2s_out.erl | 20 +----- src/xml_stream.erl | 1 + 8 files changed, 235 insertions(+), 22 deletions(-) create mode 100644 src/cyrsasl_digest.erl diff --git a/ChangeLog b/ChangeLog index 5032cca2e..65fd89058 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2003-03-12 Alexey Shchepin + + * 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 * src/cyrsasl*.erl: SASL support (currently support only PLAIN diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl index 537ad4f1b..bc6a80be9 100644 --- a/src/cyrsasl.erl +++ b/src/cyrsasl.erl @@ -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); diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl new file mode 100644 index 000000000..c4593c36d --- /dev/null +++ b/src/cyrsasl_digest.erl @@ -0,0 +1,144 @@ +%%%---------------------------------------------------------------------- +%%% File : cyrsasl_digest.erl +%%% Author : Alexey Shchepin +%%% Purpose : DIGEST-MD5 SASL mechanism +%%% Created : 11 Mar 2003 by Alexey Shchepin +%%% 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))). + + + diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index efe9e407b..e080b8028 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -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 diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index e4150ffda..27395bae6 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -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}. + diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 004ddba38..ffa9cbb17 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -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. diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index d88967b0f..458113b7e 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -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). diff --git a/src/xml_stream.erl b/src/xml_stream.erl index bb3776157..583fc7cf0 100644 --- a/src/xml_stream.erl +++ b/src/xml_stream.erl @@ -16,6 +16,7 @@ start(CallbackPid) -> spawn(?MODULE, init, [CallbackPid]). init(CallbackPid) -> + link(CallbackPid), Port = open_port({spawn, expat_erl}, [binary]), loop(CallbackPid, Port, []).