mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-28 16:34:13 +01:00
* 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:
parent
f3916bddd2
commit
8b26ac9e97
11
ChangeLog
11
ChangeLog
@ -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
|
||||
|
@ -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
144
src/cyrsasl_digest.erl
Normal 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))).
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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}.
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -56,7 +56,8 @@ 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, Pid} = apply(Module, Fun, [{gen_tcp, Socket}, Opts]),
|
||||
gen_tcp:controlling_process(Socket, Pid),
|
||||
accept(ListenSocket, Module, Fun, Opts)
|
||||
end.
|
||||
|
||||
|
@ -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).
|
||||
|
||||
|
@ -16,6 +16,7 @@ start(CallbackPid) ->
|
||||
spawn(?MODULE, init, [CallbackPid]).
|
||||
|
||||
init(CallbackPid) ->
|
||||
link(CallbackPid),
|
||||
Port = open_port({spawn, expat_erl}, [binary]),
|
||||
loop(CallbackPid, Port, []).
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user