mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Reflect cyrsasl API changes in remaining code
This commit is contained in:
parent
247ca872f6
commit
cf87c5664f
@ -221,7 +221,7 @@ process_closed(State, Reason) ->
|
|||||||
process_terminated(#{socket := Socket, jid := JID} = State,
|
process_terminated(#{socket := Socket, jid := JID} = State,
|
||||||
Reason) ->
|
Reason) ->
|
||||||
Status = format_reason(State, Reason),
|
Status = format_reason(State, Reason),
|
||||||
?INFO_MSG("(~s) Closing c2s connection for ~s: ~s",
|
?INFO_MSG("(~s) Closing c2s session for ~s: ~s",
|
||||||
[ejabberd_socket:pp(Socket), jid:to_string(JID), Status]),
|
[ejabberd_socket:pp(Socket), jid:to_string(JID), Status]),
|
||||||
Pres = #presence{type = unavailable,
|
Pres = #presence{type = unavailable,
|
||||||
status = xmpp:mk_text(Status),
|
status = xmpp:mk_text(Status),
|
||||||
@ -292,12 +292,12 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
|||||||
State1 = open_session(State#{resource => Resource}),
|
State1 = open_session(State#{resource => Resource}),
|
||||||
State2 = ejabberd_hooks:run_fold(
|
State2 = ejabberd_hooks:run_fold(
|
||||||
c2s_session_opened, LServer, State1, []),
|
c2s_session_opened, LServer, State1, []),
|
||||||
?INFO_MSG("(~s) Opened session for ~s",
|
?INFO_MSG("(~s) Opened c2s session for ~s",
|
||||||
[ejabberd_socket:pp(Socket), jid:to_string(JID)]),
|
[ejabberd_socket:pp(Socket), jid:to_string(JID)]),
|
||||||
{ok, State2};
|
{ok, State2};
|
||||||
deny ->
|
deny ->
|
||||||
ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]),
|
ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]),
|
||||||
?INFO_MSG("(~s) Forbidden session for ~s",
|
?INFO_MSG("(~s) Forbidden c2s session for ~s",
|
||||||
[ejabberd_socket:pp(Socket), jid:to_string(JID)]),
|
[ejabberd_socket:pp(Socket), jid:to_string(JID)]),
|
||||||
Txt = <<"Denied by ACL">>,
|
Txt = <<"Denied by ACL">>,
|
||||||
{error, xmpp:err_not_allowed(Txt, Lang), State}
|
{error, xmpp:err_not_allowed(Txt, Lang), State}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
-export([start/2, stop/1, depends/2, mod_opt_type/1]).
|
-export([start/2, stop/1, depends/2, mod_opt_type/1]).
|
||||||
%% Hooks
|
%% Hooks
|
||||||
-export([s2s_out_auth_result/2, s2s_out_downgraded/2,
|
-export([s2s_out_auth_result/2, s2s_out_downgraded/2,
|
||||||
s2s_in_packet/2, s2s_out_packet/2,
|
s2s_in_packet/2, s2s_out_packet/2, s2s_in_recv/3,
|
||||||
s2s_in_features/2, s2s_out_init/2, s2s_out_closed/2]).
|
s2s_in_features/2, s2s_out_init/2, s2s_out_closed/2]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
@ -52,6 +52,8 @@ start(Host, _Opts) ->
|
|||||||
s2s_in_features, 50),
|
s2s_in_features, 50),
|
||||||
ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE,
|
ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE,
|
||||||
s2s_in_features, 50),
|
s2s_in_features, 50),
|
||||||
|
ejabberd_hooks:add(s2s_in_handle_recv, Host, ?MODULE,
|
||||||
|
s2s_in_recv, 50),
|
||||||
ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE,
|
ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE,
|
||||||
s2s_in_packet, 50),
|
s2s_in_packet, 50),
|
||||||
ejabberd_hooks:add(s2s_in_authenticated_packet, Host, ?MODULE,
|
ejabberd_hooks:add(s2s_in_authenticated_packet, Host, ?MODULE,
|
||||||
@ -71,6 +73,8 @@ stop(Host) ->
|
|||||||
s2s_in_features, 50),
|
s2s_in_features, 50),
|
||||||
ejabberd_hooks:delete(s2s_in_post_auth_features, Host, ?MODULE,
|
ejabberd_hooks:delete(s2s_in_post_auth_features, Host, ?MODULE,
|
||||||
s2s_in_features, 50),
|
s2s_in_features, 50),
|
||||||
|
ejabberd_hooks:delete(s2s_in_handle_recv, Host, ?MODULE,
|
||||||
|
s2s_in_recv, 50),
|
||||||
ejabberd_hooks:delete(s2s_in_unauthenticated_packet, Host, ?MODULE,
|
ejabberd_hooks:delete(s2s_in_unauthenticated_packet, Host, ?MODULE,
|
||||||
s2s_in_packet, 50),
|
s2s_in_packet, 50),
|
||||||
ejabberd_hooks:delete(s2s_in_authenticated_packet, Host, ?MODULE,
|
ejabberd_hooks:delete(s2s_in_authenticated_packet, Host, ?MODULE,
|
||||||
@ -191,6 +195,25 @@ s2s_in_packet(State, Pkt) when is_record(Pkt, db_result);
|
|||||||
s2s_in_packet(State, _) ->
|
s2s_in_packet(State, _) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
|
s2s_in_recv(State, El, {error, Why}) ->
|
||||||
|
case xmpp:get_name(El) of
|
||||||
|
Tag when Tag == <<"db:result">>;
|
||||||
|
Tag == <<"db:verify">> ->
|
||||||
|
case xmpp:get_type(El) of
|
||||||
|
T when T /= <<"valid">>,
|
||||||
|
T /= <<"invalid">>,
|
||||||
|
T /= <<"error">> ->
|
||||||
|
Err = xmpp:make_error(El, mk_error({codec_error, Why})),
|
||||||
|
{stop, ejabberd_s2s_in:send(State, Err)};
|
||||||
|
_ ->
|
||||||
|
State
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
State
|
||||||
|
end;
|
||||||
|
s2s_in_recv(State, _El, _Pkt) ->
|
||||||
|
State.
|
||||||
|
|
||||||
s2s_out_packet(#{server := LServer,
|
s2s_out_packet(#{server := LServer,
|
||||||
remote_server := RServer,
|
remote_server := RServer,
|
||||||
db_verify := {StreamID, _Key, Pid}} = State,
|
db_verify := {StreamID, _Key, Pid}} = State,
|
||||||
@ -286,6 +309,8 @@ mk_error(forbidden) ->
|
|||||||
xmpp:err_forbidden(<<"Denied by ACL">>, ?MYLANG);
|
xmpp:err_forbidden(<<"Denied by ACL">>, ?MYLANG);
|
||||||
mk_error(host_unknown) ->
|
mk_error(host_unknown) ->
|
||||||
xmpp:err_not_allowed(<<"Host unknown">>, ?MYLANG);
|
xmpp:err_not_allowed(<<"Host unknown">>, ?MYLANG);
|
||||||
|
mk_error({codec_error, Why}) ->
|
||||||
|
xmpp:err_bad_request(xmpp:io_format_error(Why), ?MYLANG);
|
||||||
mk_error({_Class, _Reason} = Why) ->
|
mk_error({_Class, _Reason} = Why) ->
|
||||||
Txt = xmpp_stream_out:format_error(Why),
|
Txt = xmpp_stream_out:format_error(Why),
|
||||||
xmpp:err_remote_server_not_found(Txt, ?MYLANG);
|
xmpp:err_remote_server_not_found(Txt, ?MYLANG);
|
||||||
|
@ -179,16 +179,14 @@ c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) ->
|
|||||||
c2s_handle_recv(State, _, _) ->
|
c2s_handle_recv(State, _, _) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
c2s_handle_send(#{mgmt_state := MgmtState} = State, Pkt, Result)
|
c2s_handle_send(#{mgmt_state := MgmtState} = State, Pkt, _Result)
|
||||||
when MgmtState == pending; MgmtState == active ->
|
when MgmtState == pending; MgmtState == active ->
|
||||||
State1 = mgmt_queue_add(State, Pkt),
|
State1 = mgmt_queue_add(State, Pkt),
|
||||||
case Result of
|
case xmpp:is_stanza(Pkt) of
|
||||||
ok when ?is_stanza(Pkt) ->
|
true ->
|
||||||
send_rack(State1);
|
send_rack(State1);
|
||||||
ok ->
|
false ->
|
||||||
State1;
|
State1
|
||||||
{error, _} ->
|
|
||||||
transition_to_pending(State1)
|
|
||||||
end;
|
end;
|
||||||
c2s_handle_send(State, _Pkt, _Result) ->
|
c2s_handle_send(State, _Pkt, _Result) ->
|
||||||
State.
|
State.
|
||||||
@ -210,8 +208,9 @@ c2s_handle_info(#{mgmt_ack_timer := TRef, jid := JID} = State,
|
|||||||
{timeout, TRef, ack_timeout}) ->
|
{timeout, TRef, ack_timeout}) ->
|
||||||
?DEBUG("Timed out waiting for stream management acknowledgement of ~s",
|
?DEBUG("Timed out waiting for stream management acknowledgement of ~s",
|
||||||
[jid:to_string(JID)]),
|
[jid:to_string(JID)]),
|
||||||
State1 = ejabberd_c2s:close(State, _SendTrailer = false),
|
State1 = State#{stop_reason => {socket, timeout}},
|
||||||
{stop, transition_to_pending(State1)};
|
State2 = ejabberd_c2s:close(State1, _SendTrailer = false),
|
||||||
|
{stop, transition_to_pending(State2)};
|
||||||
c2s_handle_info(#{mgmt_state := pending, jid := JID} = State,
|
c2s_handle_info(#{mgmt_state := pending, jid := JID} = State,
|
||||||
{timeout, _, pending_timeout}) ->
|
{timeout, _, pending_timeout}) ->
|
||||||
?DEBUG("Timed out waiting for resumption of stream for ~s",
|
?DEBUG("Timed out waiting for resumption of stream for ~s",
|
||||||
@ -222,8 +221,8 @@ c2s_handle_info(State, _) ->
|
|||||||
|
|
||||||
c2s_closed(State, {stream, _}) ->
|
c2s_closed(State, {stream, _}) ->
|
||||||
State;
|
State;
|
||||||
c2s_closed(#{mgmt_state := active} = State, Reason) ->
|
c2s_closed(#{mgmt_state := active} = State, _Reason) ->
|
||||||
{stop, transition_to_pending(State#{stop_reason => Reason})};
|
{stop, transition_to_pending(State)};
|
||||||
c2s_closed(State, _Reason) ->
|
c2s_closed(State, _Reason) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
@ -368,10 +367,9 @@ transition_to_pending(#{mgmt_state := active, jid := JID,
|
|||||||
lserver := LServer, mgmt_timeout := Timeout} = State) ->
|
lserver := LServer, mgmt_timeout := Timeout} = State) ->
|
||||||
State1 = cancel_ack_timer(State),
|
State1 = cancel_ack_timer(State),
|
||||||
?INFO_MSG("Waiting for resumption of stream for ~s", [jid:to_string(JID)]),
|
?INFO_MSG("Waiting for resumption of stream for ~s", [jid:to_string(JID)]),
|
||||||
State2 = ejabberd_hooks:run_fold(c2s_session_pending, LServer, State1, []),
|
|
||||||
State3 = ejabberd_c2s:close(State2, _SendTrailer = false),
|
|
||||||
erlang:start_timer(timer:seconds(Timeout), self(), pending_timeout),
|
erlang:start_timer(timer:seconds(Timeout), self(), pending_timeout),
|
||||||
State3#{mgmt_state => pending};
|
State2 = State1#{mgmt_state => pending},
|
||||||
|
ejabberd_hooks:run_fold(c2s_session_pending, LServer, State2, []);
|
||||||
transition_to_pending(State) ->
|
transition_to_pending(State) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
@ -405,8 +403,8 @@ update_num_stanzas_in(State, _El) ->
|
|||||||
send_rack(#{mgmt_ack_timer := _} = State) ->
|
send_rack(#{mgmt_ack_timer := _} = State) ->
|
||||||
State;
|
State;
|
||||||
send_rack(#{mgmt_xmlns := Xmlns,
|
send_rack(#{mgmt_xmlns := Xmlns,
|
||||||
mgmt_stanzas_out := NumStanzasOut,
|
mgmt_stanzas_out := NumStanzasOut,
|
||||||
mgmt_ack_timeout := AckTimeout} = State) ->
|
mgmt_ack_timeout := AckTimeout} = State) ->
|
||||||
State1 = send(State, #sm_r{xmlns = Xmlns}),
|
State1 = send(State, #sm_r{xmlns = Xmlns}),
|
||||||
TRef = erlang:start_timer(AckTimeout, self(), ack_timeout),
|
TRef = erlang:start_timer(AckTimeout, self(), ack_timeout),
|
||||||
State1#{mgmt_ack_timer => TRef, mgmt_stanzas_req => NumStanzasOut}.
|
State1#{mgmt_ack_timer => TRef, mgmt_stanzas_req => NumStanzasOut}.
|
||||||
@ -425,16 +423,19 @@ resend_rack(State) ->
|
|||||||
|
|
||||||
-spec mgmt_queue_add(state(), xmpp_element()) -> state().
|
-spec mgmt_queue_add(state(), xmpp_element()) -> state().
|
||||||
mgmt_queue_add(#{mgmt_stanzas_out := NumStanzasOut,
|
mgmt_queue_add(#{mgmt_stanzas_out := NumStanzasOut,
|
||||||
mgmt_queue := Queue} = State, Stanza) when ?is_stanza(Stanza) ->
|
mgmt_queue := Queue} = State, Pkt) ->
|
||||||
NewNum = case NumStanzasOut of
|
case xmpp:is_stanza(Pkt) of
|
||||||
4294967295 -> 0;
|
true ->
|
||||||
Num -> Num + 1
|
NewNum = case NumStanzasOut of
|
||||||
end,
|
4294967295 -> 0;
|
||||||
Queue1 = queue_in({NewNum, p1_time_compat:timestamp(), Stanza}, Queue),
|
Num -> Num + 1
|
||||||
State1 = State#{mgmt_queue => Queue1, mgmt_stanzas_out => NewNum},
|
end,
|
||||||
check_queue_length(State1);
|
Queue1 = queue_in({NewNum, p1_time_compat:timestamp(), Pkt}, Queue),
|
||||||
mgmt_queue_add(State, _Nonza) ->
|
State1 = State#{mgmt_queue => Queue1, mgmt_stanzas_out => NewNum},
|
||||||
State.
|
check_queue_length(State1);
|
||||||
|
false ->
|
||||||
|
State
|
||||||
|
end.
|
||||||
|
|
||||||
-spec mgmt_queue_drop(state(), non_neg_integer()) -> state().
|
-spec mgmt_queue_drop(state(), non_neg_integer()) -> state().
|
||||||
mgmt_queue_drop(#{mgmt_queue := Queue} = State, NumHandled) ->
|
mgmt_queue_drop(#{mgmt_queue := Queue} = State, NumHandled) ->
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
-type state() :: map().
|
-type state() :: map().
|
||||||
-type stop_reason() :: {stream, reset | stream_error()} |
|
-type stop_reason() :: {stream, reset | {in | out, stream_error()}} |
|
||||||
{tls, term()} |
|
{tls, term()} |
|
||||||
{socket, inet:posix() | closed | timeout} |
|
{socket, inet:posix() | closed | timeout} |
|
||||||
internal_failure.
|
internal_failure.
|
||||||
@ -188,8 +188,10 @@ format_error({socket, Reason}) ->
|
|||||||
format("Connection failed: ~s", [format_inet_error(Reason)]);
|
format("Connection failed: ~s", [format_inet_error(Reason)]);
|
||||||
format_error({stream, reset}) ->
|
format_error({stream, reset}) ->
|
||||||
<<"Stream reset by peer">>;
|
<<"Stream reset by peer">>;
|
||||||
format_error({stream, #stream_error{reason = Reason, text = Txt}}) ->
|
format_error({stream, {in, #stream_error{reason = Reason, text = Txt}}}) ->
|
||||||
format("Stream failed: ~s", [format_stream_error(Reason, Txt)]);
|
format("Stream closed by peer: ~s", [format_stream_error(Reason, Txt)]);
|
||||||
|
format_error({stream, {out, #stream_error{reason = Reason, text = Txt}}}) ->
|
||||||
|
format("Stream closed by us: ~s", [format_stream_error(Reason, Txt)]);
|
||||||
format_error({tls, Reason}) ->
|
format_error({tls, Reason}) ->
|
||||||
format("TLS failed: ~w", [Reason]);
|
format("TLS failed: ~w", [Reason]);
|
||||||
format_error(internal_failure) ->
|
format_error(internal_failure) ->
|
||||||
@ -304,7 +306,7 @@ handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) ->
|
|||||||
send_element(State1, Err)
|
send_element(State1, Err)
|
||||||
end);
|
end);
|
||||||
handle_info({'$gen_event', {xmlstreamelement, El}},
|
handle_info({'$gen_event', {xmlstreamelement, El}},
|
||||||
#{xmlns := NS, lang := MyLang, mod := Mod} = State) ->
|
#{xmlns := NS, mod := Mod} = State) ->
|
||||||
noreply(
|
noreply(
|
||||||
try xmpp:decode(El, NS, [ignore_els]) of
|
try xmpp:decode(El, NS, [ignore_els]) of
|
||||||
Pkt ->
|
Pkt ->
|
||||||
@ -321,10 +323,7 @@ handle_info({'$gen_event', {xmlstreamelement, El}},
|
|||||||
end,
|
end,
|
||||||
case is_disconnected(State1) of
|
case is_disconnected(State1) of
|
||||||
true -> State1;
|
true -> State1;
|
||||||
false ->
|
false -> process_invalid_xml(State1, El, Why)
|
||||||
Txt = xmpp:io_format_error(Why),
|
|
||||||
Lang = select_lang(MyLang, xmpp:get_lang(El)),
|
|
||||||
send_error(State1, El, xmpp:err_bad_request(Txt, Lang))
|
|
||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}},
|
handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}},
|
||||||
@ -394,6 +393,33 @@ peername(SockMod, Socket) ->
|
|||||||
_ -> SockMod:peername(Socket)
|
_ -> SockMod:peername(Socket)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec process_invalid_xml(state(), fxml:xmlel(), term()) -> state().
|
||||||
|
process_invalid_xml(#{lang := MyLang} = State, El, Reason) ->
|
||||||
|
case xmpp:is_stanza(El) of
|
||||||
|
true ->
|
||||||
|
Txt = xmpp:io_format_error(Reason),
|
||||||
|
Lang = select_lang(MyLang, xmpp:get_lang(El)),
|
||||||
|
send_error(State, El, xmpp:err_bad_request(Txt, Lang));
|
||||||
|
false ->
|
||||||
|
case {xmpp:get_name(El), xmpp:get_ns(El)} of
|
||||||
|
{Tag, ?NS_SASL} when Tag == <<"auth">>;
|
||||||
|
Tag == <<"response">>;
|
||||||
|
Tag == <<"abort">> ->
|
||||||
|
Txt = xmpp:io_format_error(Reason),
|
||||||
|
Err = #sasl_failure{reason = 'malformed-request',
|
||||||
|
text = xmpp:mk_text(Txt, MyLang)},
|
||||||
|
send_element(State, Err);
|
||||||
|
{<<"starttls">>, ?NS_TLS} ->
|
||||||
|
send_element(State, #starttls_failure{});
|
||||||
|
{<<"compress">>, ?NS_COMPRESS} ->
|
||||||
|
Err = #compress_failure{reason = 'setup-failed'},
|
||||||
|
send_element(State, Err);
|
||||||
|
_ ->
|
||||||
|
%% Maybe add something more?
|
||||||
|
State
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
-spec process_stream_end(stop_reason(), state()) -> state().
|
-spec process_stream_end(stop_reason(), state()) -> state().
|
||||||
process_stream_end(_, #{stream_state := disconnected} = State) ->
|
process_stream_end(_, #{stream_state := disconnected} = State) ->
|
||||||
State;
|
State;
|
||||||
@ -423,11 +449,6 @@ process_stream(#stream_start{lang = Lang},
|
|||||||
process_stream(#stream_start{to = undefined}, #{lang := Lang} = State) ->
|
process_stream(#stream_start{to = undefined}, #{lang := Lang} = State) ->
|
||||||
Txt = <<"Missing 'to' attribute">>,
|
Txt = <<"Missing 'to' attribute">>,
|
||||||
send_element(State, xmpp:serr_improper_addressing(Txt, Lang));
|
send_element(State, xmpp:serr_improper_addressing(Txt, Lang));
|
||||||
process_stream(#stream_start{from = undefined, version = {1,0}},
|
|
||||||
#{lang := Lang, xmlns := ?NS_SERVER,
|
|
||||||
stream_encrypted := true} = State) ->
|
|
||||||
Txt = <<"Missing 'from' attribute">>,
|
|
||||||
send_element(State, xmpp:serr_invalid_from(Txt, Lang));
|
|
||||||
process_stream(#stream_start{to = #jid{luser = U, lresource = R}},
|
process_stream(#stream_start{to = #jid{luser = U, lresource = R}},
|
||||||
#{lang := Lang} = State) when U /= <<"">>; R /= <<"">> ->
|
#{lang := Lang} = State) when U /= <<"">>; R /= <<"">> ->
|
||||||
Txt = <<"Improper 'to' attribute">>,
|
Txt = <<"Improper 'to' attribute">>,
|
||||||
@ -450,9 +471,10 @@ process_stream(#stream_start{to = #jid{server = Server, lserver = LServer},
|
|||||||
true ->
|
true ->
|
||||||
State
|
State
|
||||||
end,
|
end,
|
||||||
State2 = if NS == ?NS_SERVER andalso Encrypted ->
|
State2 = case From of
|
||||||
State1#{remote_server => From#jid.lserver};
|
#jid{lserver = RemoteServer} when NS == ?NS_SERVER ->
|
||||||
true ->
|
State1#{remote_server => RemoteServer};
|
||||||
|
_ ->
|
||||||
State1
|
State1
|
||||||
end,
|
end,
|
||||||
State3 = try Mod:handle_stream_start(StreamStart, State2)
|
State3 = try Mod:handle_stream_start(StreamStart, State2)
|
||||||
@ -517,7 +539,7 @@ process_element(Pkt, #{stream_state := StateName, lang := Lang} = State) ->
|
|||||||
#handshake{} ->
|
#handshake{} ->
|
||||||
State;
|
State;
|
||||||
#stream_error{} ->
|
#stream_error{} ->
|
||||||
process_stream_end({stream, Pkt}, State);
|
process_stream_end({stream, {in, Pkt}}, State);
|
||||||
_ when StateName == wait_for_sasl_request;
|
_ when StateName == wait_for_sasl_request;
|
||||||
StateName == wait_for_handshake;
|
StateName == wait_for_handshake;
|
||||||
StateName == wait_for_sasl_response ->
|
StateName == wait_for_sasl_response ->
|
||||||
@ -707,35 +729,34 @@ process_starttls_failure(Why, State) ->
|
|||||||
-spec process_sasl_request(sasl_auth(), state()) -> state().
|
-spec process_sasl_request(sasl_auth(), state()) -> state().
|
||||||
process_sasl_request(#sasl_auth{mechanism = Mech, text = ClientIn},
|
process_sasl_request(#sasl_auth{mechanism = Mech, text = ClientIn},
|
||||||
#{mod := Mod, lserver := LServer} = State) ->
|
#{mod := Mod, lserver := LServer} = State) ->
|
||||||
GetPW = try Mod:get_password_fun(State)
|
State1 = State#{sasl_mech => Mech},
|
||||||
catch _:undef -> fun(_) -> false end
|
|
||||||
end,
|
|
||||||
CheckPW = try Mod:check_password_fun(State)
|
|
||||||
catch _:undef -> fun(_, _, _) -> false end
|
|
||||||
end,
|
|
||||||
CheckPWDigest = try Mod:check_password_digest_fun(State)
|
|
||||||
catch _:undef -> fun(_, _, _, _, _) -> false end
|
|
||||||
end,
|
|
||||||
SASLState = cyrsasl:server_new(<<"jabber">>, LServer, <<"">>, [],
|
|
||||||
GetPW, CheckPW, CheckPWDigest),
|
|
||||||
State1 = State#{sasl_state => SASLState, sasl_mech => Mech},
|
|
||||||
Mechs = get_sasl_mechanisms(State1),
|
Mechs = get_sasl_mechanisms(State1),
|
||||||
SASLResult = case lists:member(Mech, Mechs) of
|
case lists:member(Mech, Mechs) of
|
||||||
true when Mech == <<"EXTERNAL">> ->
|
true when Mech == <<"EXTERNAL">> ->
|
||||||
case xmpp_stream_pkix:authenticate(State1, ClientIn) of
|
Res = case xmpp_stream_pkix:authenticate(State1, ClientIn) of
|
||||||
{ok, Peer} ->
|
{ok, Peer} ->
|
||||||
{ok, [{auth_module, pkix},
|
{ok, [{auth_module, pkix}, {username, Peer}]};
|
||||||
{username, Peer}]};
|
{error, Reason, Peer} ->
|
||||||
{error, _Reason, Peer} ->
|
{error, Reason, Peer}
|
||||||
%% TODO: return meaningful error
|
end,
|
||||||
{error, 'not-authorized', Peer}
|
process_sasl_result(Res, State1);
|
||||||
end;
|
true ->
|
||||||
true ->
|
GetPW = try Mod:get_password_fun(State1)
|
||||||
cyrsasl:server_start(SASLState, Mech, ClientIn);
|
catch _:undef -> fun(_) -> false end
|
||||||
false ->
|
end,
|
||||||
{error, 'invalid-mechanism'}
|
CheckPW = try Mod:check_password_fun(State1)
|
||||||
end,
|
catch _:undef -> fun(_, _, _) -> false end
|
||||||
process_sasl_result(SASLResult, State1).
|
end,
|
||||||
|
CheckPWDigest = try Mod:check_password_digest_fun(State1)
|
||||||
|
catch _:undef -> fun(_, _, _, _, _) -> false end
|
||||||
|
end,
|
||||||
|
SASLState = cyrsasl:server_new(<<"jabber">>, LServer, <<"">>, [],
|
||||||
|
GetPW, CheckPW, CheckPWDigest),
|
||||||
|
Res = cyrsasl:server_start(SASLState, Mech, ClientIn),
|
||||||
|
process_sasl_result(Res, State1#{sasl_state => SASLState});
|
||||||
|
false ->
|
||||||
|
process_sasl_result({error, unsupported_mechanism, <<"">>}, State1)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec process_sasl_response(sasl_response(), state()) -> state().
|
-spec process_sasl_response(sasl_response(), state()) -> state().
|
||||||
process_sasl_response(#sasl_response{text = ClientIn},
|
process_sasl_response(#sasl_response{text = ClientIn},
|
||||||
@ -751,9 +772,7 @@ process_sasl_result({ok, Props, ServerOut}, State) ->
|
|||||||
process_sasl_result({continue, ServerOut, NewSASLState}, State) ->
|
process_sasl_result({continue, ServerOut, NewSASLState}, State) ->
|
||||||
process_sasl_continue(ServerOut, NewSASLState, State);
|
process_sasl_continue(ServerOut, NewSASLState, State);
|
||||||
process_sasl_result({error, Reason, User}, State) ->
|
process_sasl_result({error, Reason, User}, State) ->
|
||||||
process_sasl_failure(Reason, User, State);
|
process_sasl_failure(Reason, User, State).
|
||||||
process_sasl_result({error, Reason}, State) ->
|
|
||||||
process_sasl_failure(Reason, <<"">>, State).
|
|
||||||
|
|
||||||
-spec process_sasl_success([cyrsasl:sasl_property()], binary(), state()) -> state().
|
-spec process_sasl_success([cyrsasl:sasl_property()], binary(), state()) -> state().
|
||||||
process_sasl_success(Props, ServerOut,
|
process_sasl_success(Props, ServerOut,
|
||||||
@ -790,18 +809,20 @@ process_sasl_continue(ServerOut, NewSASLState, State) ->
|
|||||||
send_element(State1, #sasl_challenge{text = ServerOut}).
|
send_element(State1, #sasl_challenge{text = ServerOut}).
|
||||||
|
|
||||||
-spec process_sasl_failure(atom(), binary(), state()) -> state().
|
-spec process_sasl_failure(atom(), binary(), state()) -> state().
|
||||||
process_sasl_failure(Reason, User,
|
process_sasl_failure(Err, User,
|
||||||
#{mod := Mod, sasl_mech := Mech} = State) ->
|
#{mod := Mod, sasl_mech := Mech, lang := Lang} = State) ->
|
||||||
State1 = try Mod:handle_auth_failure(User, Mech, Reason, State)
|
{Reason, Text} = format_sasl_error(Mech, Err),
|
||||||
|
State1 = try Mod:handle_auth_failure(User, Mech, Text, State)
|
||||||
catch _:undef -> State
|
catch _:undef -> State
|
||||||
end,
|
end,
|
||||||
State2 = maps:remove(sasl_state, maps:remove(sasl_mech, State1)),
|
State2 = maps:remove(sasl_state, maps:remove(sasl_mech, State1)),
|
||||||
State3 = State2#{stream_state => wait_for_sasl_request},
|
State3 = State2#{stream_state => wait_for_sasl_request},
|
||||||
send_element(State3, #sasl_failure{reason = Reason}).
|
send_element(State3, #sasl_failure{reason = Reason,
|
||||||
|
text = xmpp:mk_text(Text, Lang)}).
|
||||||
|
|
||||||
-spec process_sasl_abort(state()) -> state().
|
-spec process_sasl_abort(state()) -> state().
|
||||||
process_sasl_abort(State) ->
|
process_sasl_abort(State) ->
|
||||||
process_sasl_failure('aborted', <<"">>, State).
|
process_sasl_failure(aborted, <<"">>, State).
|
||||||
|
|
||||||
-spec send_features(state()) -> state().
|
-spec send_features(state()) -> state().
|
||||||
send_features(#{stream_version := {1,0},
|
send_features(#{stream_version := {1,0},
|
||||||
@ -985,13 +1006,17 @@ send_element(#{xmlns := NS, mod := Mod} = State, Pkt) ->
|
|||||||
State1 = try Mod:handle_send(Pkt, Result, State)
|
State1 = try Mod:handle_send(Pkt, Result, State)
|
||||||
catch _:undef -> State
|
catch _:undef -> State
|
||||||
end,
|
end,
|
||||||
case Result of
|
case is_disconnected(State1) of
|
||||||
_ when is_record(Pkt, stream_error) ->
|
true -> State1;
|
||||||
process_stream_end({stream, Pkt}, State1);
|
false ->
|
||||||
ok ->
|
case Result of
|
||||||
State1;
|
_ when is_record(Pkt, stream_error) ->
|
||||||
{error, Why} ->
|
process_stream_end({stream, {out, Pkt}}, State1);
|
||||||
process_stream_end({socket, Why}, State1)
|
ok ->
|
||||||
|
State1;
|
||||||
|
{error, Why} ->
|
||||||
|
process_stream_end({socket, Why}, State1)
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec send_error(state(), xmpp_element(), stanza_error()) -> state().
|
-spec send_error(state(), xmpp_element(), stanza_error()) -> state().
|
||||||
@ -1025,6 +1050,8 @@ send_text(_, _) ->
|
|||||||
{error, closed}.
|
{error, closed}.
|
||||||
|
|
||||||
-spec close_socket(state()) -> state().
|
-spec close_socket(state()) -> state().
|
||||||
|
close_socket(#{stream_state := disconnected} = State) ->
|
||||||
|
State;
|
||||||
close_socket(#{sockmod := SockMod, socket := Socket} = State) ->
|
close_socket(#{sockmod := SockMod, socket := Socket} = State) ->
|
||||||
SockMod:close(Socket),
|
SockMod:close(Socket),
|
||||||
State#{stream_timeout => infinity,
|
State#{stream_timeout => infinity,
|
||||||
@ -1052,6 +1079,7 @@ format_inet_error(Reason) ->
|
|||||||
-spec format_stream_error(atom() | 'see-other-host'(), undefined | text()) -> string().
|
-spec format_stream_error(atom() | 'see-other-host'(), undefined | text()) -> string().
|
||||||
format_stream_error(Reason, Txt) ->
|
format_stream_error(Reason, Txt) ->
|
||||||
Slogan = case Reason of
|
Slogan = case Reason of
|
||||||
|
undefined -> "no reason";
|
||||||
#'see-other-host'{} -> "see-other-host";
|
#'see-other-host'{} -> "see-other-host";
|
||||||
_ -> atom_to_list(Reason)
|
_ -> atom_to_list(Reason)
|
||||||
end,
|
end,
|
||||||
@ -1062,6 +1090,12 @@ format_stream_error(Reason, Txt) ->
|
|||||||
binary_to_list(Data) ++ " (" ++ Slogan ++ ")"
|
binary_to_list(Data) ++ " (" ++ Slogan ++ ")"
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec format_sasl_error(cyrsasl:mechanism(), atom()) -> {atom(), binary()}.
|
||||||
|
format_sasl_error(<<"EXTERNAL">>, Err) ->
|
||||||
|
xmpp_stream_pkix:format_error(Err);
|
||||||
|
format_sasl_error(Mech, Err) ->
|
||||||
|
cyrsasl:format_error(Mech, Err).
|
||||||
|
|
||||||
-spec format(io:format(), list()) -> binary().
|
-spec format(io:format(), list()) -> binary().
|
||||||
format(Fmt, Args) ->
|
format(Fmt, Args) ->
|
||||||
iolist_to_binary(io_lib:format(Fmt, Args)).
|
iolist_to_binary(io_lib:format(Fmt, Args)).
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%% @copyright (C) 2016, Evgeny Khramtsov
|
|
||||||
%%% @doc
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%% Created : 14 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
%%% Created : 14 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||||
|
%%%
|
||||||
|
%%% This program is free software; you can redistribute it and/or
|
||||||
|
%%% modify it under the terms of the GNU General Public License as
|
||||||
|
%%% published by the Free Software Foundation; either version 2 of the
|
||||||
|
%%% License, or (at your option) any later version.
|
||||||
|
%%%
|
||||||
|
%%% This program is distributed in the hope that it will be useful,
|
||||||
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
%%% General Public License for more details.
|
||||||
|
%%%
|
||||||
|
%%% You should have received a copy of the GNU General Public License along
|
||||||
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
%%%
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(xmpp_stream_out).
|
-module(xmpp_stream_out).
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
@ -39,7 +52,7 @@
|
|||||||
-type network_error() :: {error, inet:posix() | inet_res:res_error()}.
|
-type network_error() :: {error, inet:posix() | inet_res:res_error()}.
|
||||||
-type stop_reason() :: {idna, bad_string} |
|
-type stop_reason() :: {idna, bad_string} |
|
||||||
{dns, inet:posix() | inet_res:res_error()} |
|
{dns, inet:posix() | inet_res:res_error()} |
|
||||||
{stream, reset | stream_error()} |
|
{stream, reset | {in | out, stream_error()}} |
|
||||||
{tls, term()} |
|
{tls, term()} |
|
||||||
{pkix, binary()} |
|
{pkix, binary()} |
|
||||||
{auth, atom() | binary() | string()} |
|
{auth, atom() | binary() | string()} |
|
||||||
@ -135,7 +148,7 @@ change_shaper(_, _) ->
|
|||||||
|
|
||||||
-spec format_error(stop_reason()) -> binary().
|
-spec format_error(stop_reason()) -> binary().
|
||||||
format_error({idna, _}) ->
|
format_error({idna, _}) ->
|
||||||
<<"Not an IDN hostname">>;
|
<<"Remote domain is not an IDN hostname">>;
|
||||||
format_error({dns, Reason}) ->
|
format_error({dns, Reason}) ->
|
||||||
format("DNS lookup failed: ~s", [format_inet_error(Reason)]);
|
format("DNS lookup failed: ~s", [format_inet_error(Reason)]);
|
||||||
format_error({socket, Reason}) ->
|
format_error({socket, Reason}) ->
|
||||||
@ -144,8 +157,10 @@ format_error({pkix, Reason}) ->
|
|||||||
format("Peer certificate rejected: ~s", [Reason]);
|
format("Peer certificate rejected: ~s", [Reason]);
|
||||||
format_error({stream, reset}) ->
|
format_error({stream, reset}) ->
|
||||||
<<"Stream reset by peer">>;
|
<<"Stream reset by peer">>;
|
||||||
format_error({stream, #stream_error{reason = Reason, text = Txt}}) ->
|
format_error({stream, {in, #stream_error{reason = Reason, text = Txt}}}) ->
|
||||||
format("Stream failed: ~s", [format_stream_error(Reason, Txt)]);
|
format("Stream closed by peer: ~s", [format_stream_error(Reason, Txt)]);
|
||||||
|
format_error({stream, {out, #stream_error{reason = Reason, text = Txt}}}) ->
|
||||||
|
format("Stream closed by us: ~s", [format_stream_error(Reason, Txt)]);
|
||||||
format_error({tls, Reason}) ->
|
format_error({tls, Reason}) ->
|
||||||
format("TLS failed: ~w", [Reason]);
|
format("TLS failed: ~w", [Reason]);
|
||||||
format_error({auth, Reason}) ->
|
format_error({auth, Reason}) ->
|
||||||
@ -264,7 +279,7 @@ handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) ->
|
|||||||
send_element(State1, Err)
|
send_element(State1, Err)
|
||||||
end);
|
end);
|
||||||
handle_info({'$gen_event', {xmlstreamelement, El}},
|
handle_info({'$gen_event', {xmlstreamelement, El}},
|
||||||
#{xmlns := NS, lang := MyLang, mod := Mod} = State) ->
|
#{xmlns := NS, mod := Mod} = State) ->
|
||||||
noreply(
|
noreply(
|
||||||
try xmpp:decode(El, NS, [ignore_els]) of
|
try xmpp:decode(El, NS, [ignore_els]) of
|
||||||
Pkt ->
|
Pkt ->
|
||||||
@ -281,10 +296,7 @@ handle_info({'$gen_event', {xmlstreamelement, El}},
|
|||||||
end,
|
end,
|
||||||
case is_disconnected(State1) of
|
case is_disconnected(State1) of
|
||||||
true -> State1;
|
true -> State1;
|
||||||
false ->
|
false -> process_invalid_xml(State1, El, Why)
|
||||||
Txt = xmpp:io_format_error(Why),
|
|
||||||
Lang = select_lang(MyLang, xmpp:get_lang(El)),
|
|
||||||
send_error(State1, El, xmpp:err_bad_request(Txt, Lang))
|
|
||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}},
|
handle_info({'$gen_all_state_event', {xmlstreamcdata, Data}},
|
||||||
@ -347,6 +359,17 @@ new_id() ->
|
|||||||
is_disconnected(#{stream_state := StreamState}) ->
|
is_disconnected(#{stream_state := StreamState}) ->
|
||||||
StreamState == disconnected.
|
StreamState == disconnected.
|
||||||
|
|
||||||
|
-spec process_invalid_xml(state(), fxml:xmlel(), term()) -> state().
|
||||||
|
process_invalid_xml(#{lang := MyLang} = State, El, Reason) ->
|
||||||
|
case xmpp:is_stanza(El) of
|
||||||
|
true ->
|
||||||
|
Txt = xmpp:io_format_error(Reason),
|
||||||
|
Lang = select_lang(MyLang, xmpp:get_lang(El)),
|
||||||
|
send_error(State, El, xmpp:err_bad_request(Txt, Lang));
|
||||||
|
false ->
|
||||||
|
State
|
||||||
|
end.
|
||||||
|
|
||||||
-spec process_stream_end(stop_reason(), state()) -> state().
|
-spec process_stream_end(stop_reason(), state()) -> state().
|
||||||
process_stream_end(_, #{stream_state := disconnected} = State) ->
|
process_stream_end(_, #{stream_state := disconnected} = State) ->
|
||||||
State;
|
State;
|
||||||
@ -394,7 +417,7 @@ process_element(Pkt, #{stream_state := StateName} = State) ->
|
|||||||
#sasl_failure{} when StateName == wait_for_sasl_response ->
|
#sasl_failure{} when StateName == wait_for_sasl_response ->
|
||||||
process_sasl_failure(Pkt, State);
|
process_sasl_failure(Pkt, State);
|
||||||
#stream_error{} ->
|
#stream_error{} ->
|
||||||
process_stream_end({stream, Pkt}, State);
|
process_stream_end({stream, {in, Pkt}}, State);
|
||||||
_ when is_record(Pkt, stream_features);
|
_ when is_record(Pkt, stream_features);
|
||||||
is_record(Pkt, starttls_proceed);
|
is_record(Pkt, starttls_proceed);
|
||||||
is_record(Pkt, starttls);
|
is_record(Pkt, starttls);
|
||||||
@ -612,7 +635,7 @@ send_element(#{xmlns := NS, mod := Mod} = State, Pkt) ->
|
|||||||
false ->
|
false ->
|
||||||
case send_text(State1, Data) of
|
case send_text(State1, Data) of
|
||||||
_ when is_record(Pkt, stream_error) ->
|
_ when is_record(Pkt, stream_error) ->
|
||||||
process_stream_end({stream, Pkt}, State1);
|
process_stream_end({stream, {out, Pkt}}, State1);
|
||||||
ok ->
|
ok ->
|
||||||
State1;
|
State1;
|
||||||
{error, Why} ->
|
{error, Why} ->
|
||||||
@ -650,6 +673,8 @@ send_trailer(State) ->
|
|||||||
close_socket(State).
|
close_socket(State).
|
||||||
|
|
||||||
-spec close_socket(state()) -> state().
|
-spec close_socket(state()) -> state().
|
||||||
|
close_socket(#{stream_state := disconnected} = State) ->
|
||||||
|
State;
|
||||||
close_socket(State) ->
|
close_socket(State) ->
|
||||||
case State of
|
case State of
|
||||||
#{sockmod := SockMod, socket := Socket} ->
|
#{sockmod := SockMod, socket := Socket} ->
|
||||||
@ -674,6 +699,7 @@ format_inet_error(Reason) ->
|
|||||||
-spec format_stream_error(atom() | 'see-other-host'(), undefined | text()) -> string().
|
-spec format_stream_error(atom() | 'see-other-host'(), undefined | text()) -> string().
|
||||||
format_stream_error(Reason, Txt) ->
|
format_stream_error(Reason, Txt) ->
|
||||||
Slogan = case Reason of
|
Slogan = case Reason of
|
||||||
|
undefined -> "no reason";
|
||||||
#'see-other-host'{} -> "see-other-host";
|
#'see-other-host'{} -> "see-other-host";
|
||||||
_ -> atom_to_list(Reason)
|
_ -> atom_to_list(Reason)
|
||||||
end,
|
end,
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
-module(xmpp_stream_pkix).
|
-module(xmpp_stream_pkix).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([authenticate/1, authenticate/2]).
|
-export([authenticate/1, authenticate/2, format_error/1]).
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
-include_lib("public_key/include/public_key.hrl").
|
-include_lib("public_key/include/public_key.hrl").
|
||||||
@ -19,21 +19,24 @@
|
|||||||
%%% API
|
%%% API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
-spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state())
|
-spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state())
|
||||||
-> {ok, binary()} | {error, binary(), binary()}.
|
-> {ok, binary()} | {error, atom(), binary()}.
|
||||||
authenticate(State) ->
|
authenticate(State) ->
|
||||||
authenticate(State, <<"">>).
|
authenticate(State, <<"">>).
|
||||||
|
|
||||||
-spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state(), binary())
|
-spec authenticate(xmpp_stream_in:state() | xmpp_stream_out:state(), binary())
|
||||||
-> {ok, binary()} | {error, binary(), binary()}.
|
-> {ok, binary()} | {error, atom(), binary()}.
|
||||||
authenticate(#{xmlns := ?NS_SERVER, remote_server := Peer,
|
authenticate(#{xmlns := ?NS_SERVER, sockmod := SockMod,
|
||||||
sockmod := SockMod, socket := Socket}, _Authzid) ->
|
socket := Socket} = State, Authzid) ->
|
||||||
|
Peer = try maps:get(remote_server, State)
|
||||||
|
catch _:{badkey, _} -> Authzid
|
||||||
|
end,
|
||||||
case SockMod:get_peer_certificate(Socket) of
|
case SockMod:get_peer_certificate(Socket) of
|
||||||
{ok, Cert} ->
|
{ok, Cert} ->
|
||||||
case SockMod:get_verify_result(Socket) of
|
case SockMod:get_verify_result(Socket) of
|
||||||
0 ->
|
0 ->
|
||||||
case ejabberd_idna:domain_utf8_to_ascii(Peer) of
|
case ejabberd_idna:domain_utf8_to_ascii(Peer) of
|
||||||
false ->
|
false ->
|
||||||
{error, <<"Cannot decode remote server name">>, Peer};
|
{error, idna_failed, Peer};
|
||||||
AsciiPeer ->
|
AsciiPeer ->
|
||||||
case lists:any(
|
case lists:any(
|
||||||
fun(D) -> match_domain(AsciiPeer, D) end,
|
fun(D) -> match_domain(AsciiPeer, D) end,
|
||||||
@ -41,20 +44,34 @@ authenticate(#{xmlns := ?NS_SERVER, remote_server := Peer,
|
|||||||
true ->
|
true ->
|
||||||
{ok, Peer};
|
{ok, Peer};
|
||||||
false ->
|
false ->
|
||||||
{error, <<"Certificate host name mismatch">>, Peer}
|
{error, hostname_mismatch, Peer}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
VerifyRes ->
|
VerifyRes ->
|
||||||
{error, fast_tls:get_cert_verify_string(VerifyRes, Cert), Peer}
|
%% TODO: return atomic errors
|
||||||
|
%% This should be improved in fast_tls
|
||||||
|
Reason = fast_tls:get_cert_verify_string(VerifyRes, Cert),
|
||||||
|
{error, erlang:binary_to_atom(Reason, utf8), Peer}
|
||||||
end;
|
end;
|
||||||
{error, _Reason} ->
|
{error, _Reason} ->
|
||||||
{error, <<"Cannot get peer certificate">>, Peer};
|
{error, get_cert_failed, Peer};
|
||||||
error ->
|
error ->
|
||||||
{error, <<"Cannot get peer certificate">>, Peer}
|
{error, get_cert_failed, Peer}
|
||||||
end;
|
end;
|
||||||
authenticate(_State, _Authzid) ->
|
authenticate(_State, _Authzid) ->
|
||||||
%% TODO: client PKIX authentication
|
%% TODO: client PKIX authentication
|
||||||
{error, <<"Client certificate verification not implemented">>, <<"">>}.
|
{error, client_not_supported, <<"">>}.
|
||||||
|
|
||||||
|
format_error(idna_failed) ->
|
||||||
|
{'bad-protocol', <<"Remote domain is not an IDN hostname">>};
|
||||||
|
format_error(hostname_mismatch) ->
|
||||||
|
{'not-authorized', <<"Certificate host name mismatch">>};
|
||||||
|
format_error(get_cert_failed) ->
|
||||||
|
{'bad-protocol', <<"Failed to get peer certificate">>};
|
||||||
|
format_error(client_not_supported) ->
|
||||||
|
{'invalid-mechanism', <<"Client certificate verification is not supported">>};
|
||||||
|
format_error(Other) ->
|
||||||
|
{'not-authorized', erlang:atom_to_binary(Other, utf8)}.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
|
Loading…
Reference in New Issue
Block a user