diff options
Diffstat (limited to 'src/ejabberd_s2s_out.erl')
-rw-r--r-- | src/ejabberd_s2s_out.erl | 131 |
1 files changed, 84 insertions, 47 deletions
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index a5acdad95..052729314 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -5,7 +5,7 @@ %%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% %%% -%%% ejabberd, Copyright (C) 2002-2013 ProcessOne +%%% ejabberd, Copyright (C) 2002-2014 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -17,10 +17,9 @@ %%% 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., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA +%%% 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. %%% %%%---------------------------------------------------------------------- @@ -70,6 +69,7 @@ use_v10 = true :: boolean(), tls = false :: boolean(), tls_required = false :: boolean(), + tls_certverify = false :: boolean(), tls_enabled = false :: boolean(), tls_options = [connect] :: list(), authenticated = false :: boolean(), @@ -161,28 +161,27 @@ stop_connection(Pid) -> p1_fsm:send_event(Pid, closed). init([From, Server, Type]) -> process_flag(trap_exit, true), ?DEBUG("started: ~p", [{From, Server, Type}]), - {TLS, TLSRequired} = case - ejabberd_config:get_option( - s2s_use_starttls, - fun(true) -> true; - (false) -> false; - (optional) -> optional; - (required) -> required; - (required_trusted) -> required_trusted - end) - of - UseTls - when (UseTls == undefined) or - (UseTls == false) -> - {false, false}; - UseTls - when (UseTls == true) or (UseTls == optional) -> - {true, false}; - UseTls - when (UseTls == required) or - (UseTls == required_trusted) -> - {true, true} - end, + {TLS, TLSRequired, TLSCertverify} = + case ejabberd_config:get_option( + s2s_use_starttls, + fun(true) -> true; + (false) -> false; + (optional) -> optional; + (required) -> required; + (required_trusted) -> required_trusted + end) + of + UseTls + when (UseTls == undefined) or (UseTls == false) -> + {false, false, false}; + UseTls + when (UseTls == true) or (UseTls == optional) -> + {true, false, false}; + required -> + {true, true, false}; + required_trusted -> + {true, true, true} + end, UseV10 = TLS, TLSOpts1 = case ejabberd_config:get_option( @@ -196,13 +195,25 @@ init([From, Server, Type]) -> undefined -> TLSOpts1; Ciphers -> [{ciphers, Ciphers} | TLSOpts1] end, + TLSOpts3 = case ejabberd_config:get_option( + s2s_protocol_options, + fun (Options) -> + [_|O] = lists:foldl( + fun(X, Acc) -> X ++ Acc end, [], + [["|" | binary_to_list(Opt)] || Opt <- Options, is_binary(Opt)] + ), + iolist_to_binary(O) + end) of + undefined -> TLSOpts2; + ProtocolOpts -> [{protocol_options, ProtocolOpts} | TLSOpts2] + end, TLSOpts = case ejabberd_config:get_option( {s2s_tls_compression, From}, fun(true) -> true; (false) -> false end, true) of - false -> [compression_none | TLSOpts2]; - true -> TLSOpts2 + false -> [compression_none | TLSOpts3]; + true -> TLSOpts3 end, {New, Verify} = case Type of {new, Key} -> {Key, false}; @@ -212,9 +223,9 @@ init([From, Server, Type]) -> Timer = erlang:start_timer(?S2STIMEOUT, self(), []), {ok, open_socket, #state{use_v10 = UseV10, tls = TLS, - tls_required = TLSRequired, tls_options = TLSOpts, - queue = queue:new(), myname = From, server = Server, - new = New, verify = Verify, timer = Timer}}. + tls_required = TLSRequired, tls_certverify = TLSCertverify, + tls_options = TLSOpts, queue = queue:new(), myname = From, + server = Server, new = New, verify = Verify, timer = Timer}}. %%---------------------------------------------------------------------- %% Func: StateName/2 @@ -334,35 +345,57 @@ open_socket2(Type, Addr, Port) -> wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> + {CertCheckRes, CertCheckMsg, NewStateData} = + if StateData#state.tls_certverify, StateData#state.tls_enabled -> + {Res, Msg} = + ejabberd_s2s:check_peer_certificate(ejabberd_socket, + StateData#state.socket, + StateData#state.server), + ?DEBUG("Certificate verification result for ~s: ~s", + [StateData#state.server, Msg]), + {Res, Msg, StateData#state{tls_certverify = false}}; + true -> + {no_verify, <<"Not verified">>, StateData} + end, case {xml:get_attr_s(<<"xmlns">>, Attrs), xml:get_attr_s(<<"xmlns:db">>, Attrs), xml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>} of + _ when CertCheckRes == error -> + send_text(NewStateData, + <<(xml:element_to_binary(?SERRT_POLICY_VIOLATION(<<"en">>, + CertCheckMsg)))/binary, + (?STREAM_TRAILER)/binary>>), + ?INFO_MSG("Closing s2s connection: ~s -> ~s (~s)", + [NewStateData#state.myname, + NewStateData#state.server, + CertCheckMsg]), + {stop, normal, NewStateData}; {<<"jabber:server">>, <<"jabber:server:dialback">>, false} -> - send_db_request(StateData); + send_db_request(NewStateData); {<<"jabber:server">>, <<"jabber:server:dialback">>, true} - when StateData#state.use_v10 -> - {next_state, wait_for_features, StateData, ?FSMTIMEOUT}; + when NewStateData#state.use_v10 -> + {next_state, wait_for_features, NewStateData, ?FSMTIMEOUT}; %% Clause added to handle Tigase's workaround for an old ejabberd bug: {<<"jabber:server">>, <<"jabber:server:dialback">>, true} - when not StateData#state.use_v10 -> - send_db_request(StateData); + when not NewStateData#state.use_v10 -> + send_db_request(NewStateData); {<<"jabber:server">>, <<"">>, true} - when StateData#state.use_v10 -> + when NewStateData#state.use_v10 -> {next_state, wait_for_features, - StateData#state{db_enabled = false}, ?FSMTIMEOUT}; + NewStateData#state{db_enabled = false}, ?FSMTIMEOUT}; {NSProvided, DB, _} -> - send_text(StateData, ?INVALID_NAMESPACE_ERR), + send_text(NewStateData, ?INVALID_NAMESPACE_ERR), ?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid " "namespace).~nNamespace provided: ~p~nNamespac" "e expected: \"jabber:server\"~nxmlns:db " "provided: ~p~nAll attributes: ~p", - [StateData#state.myname, StateData#state.server, + [NewStateData#state.myname, NewStateData#state.server, NSProvided, DB, Attrs]), - {stop, normal, StateData} + {stop, normal, NewStateData} end; wait_for_stream({xmlstreamerror, _}, StateData) -> send_text(StateData, @@ -559,15 +592,19 @@ wait_for_features({xmlstreamelement, El}, StateData) -> if not SASLEXT and not StartTLS and StateData#state.authenticated -> send_queue(StateData, StateData#state.queue), - ?INFO_MSG("Connection established: ~s -> ~s", - [StateData#state.myname, StateData#state.server]), + ?INFO_MSG("Connection established: ~s -> ~s with " + "SASL EXTERNAL and TLS=~p", + [StateData#state.myname, StateData#state.server, + StateData#state.tls_enabled]), ejabberd_hooks:run(s2s_connect_hook, [StateData#state.myname, StateData#state.server]), {next_state, stream_established, StateData#state{queue = queue:new()}}; SASLEXT and StateData#state.try_auth and - (StateData#state.new /= false) -> + (StateData#state.new /= false) and + (StateData#state.tls_enabled or + not StateData#state.tls_required) -> send_element(StateData, #xmlel{name = <<"auth">>, attrs = @@ -725,8 +762,8 @@ wait_for_starttls_proceed({xmlstreamelement, El}, tls_options = TLSOpts}, send_text(NewStateData, io_lib:format(?STREAM_HEADER, - [StateData#state.myname, - StateData#state.server, + [NewStateData#state.myname, + NewStateData#state.server, <<" version='1.0'">>])), {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT}; |