Skip to content

Instantly share code, notes, and snippets.

@weiss
Last active August 29, 2015 14:00

Revisions

  1. weiss renamed this gist Apr 28, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. weiss created this gist Apr 28, 2014.
    128 changes: 128 additions & 0 deletions verify-cert-for-s2s-out
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,128 @@
    diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl
    index 057c60a..eb98943 100644
    --- a/src/ejabberd_s2s.erl
    +++ b/src/ejabberd_s2s.erl
    @@ -207,6 +216,31 @@ try_register(FromTo) ->
    dirty_get_connections() ->
    mnesia:dirty_all_keys(s2s).

    +check_peer_certificate(SockMod, Sock, Peer) ->
    + case SockMod:get_peer_certificate(Sock) of
    + {ok, Cert} ->
    + case SockMod:get_verify_result(Sock) of
    + 0 ->
    + case idna:domain_utf8_to_ascii(Peer) of
    + false ->
    + {error, <<"Cannot decode remote server name">>};
    + AsciiPeer ->
    + case
    + lists:any(fun(D) -> match_domain(AsciiPeer, D) end,
    + get_cert_domains(Cert)) of
    + true ->
    + {ok, <<"Verification successful">>};
    + false ->
    + {error, <<"Certificate host name mismatch">>}
    + end
    + end;
    + VerifyRes ->
    + {error, p1_tls:get_cert_verify_string(VerifyRes, Cert)}
    + end;
    + error ->
    + {error, <<"Cannot get peer certificate">>}
    + end.
    +
    %%====================================================================
    %% gen_server callbacks
    %%====================================================================
    diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
    index 3eb0b71..c490704 100644
    --- a/src/ejabberd_s2s_in.erl
    +++ b/src/ejabberd_s2s_in.erl
    @@ -227,45 +218,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
    Auth = if StateData#state.tls_enabled ->
    case jlib:nameprep(xml:get_attr_s(<<"from">>, Attrs)) of
    From when From /= <<"">>, From /= error ->
    - case
    - (StateData#state.sockmod):get_peer_certificate(StateData#state.socket)
    - of
    - {ok, Cert} ->
    - case
    - (StateData#state.sockmod):get_verify_result(StateData#state.socket)
    - of
    - 0 ->
    - case
    - idna:domain_utf8_to_ascii(From)
    - of
    - false ->
    - {error, From,
    - <<"Cannot decode 'from' attribute">>};
    - PCAuthDomain ->
    - case
    - lists:any(fun (D) ->
    - match_domain(PCAuthDomain,
    - D)
    - end,
    - get_cert_domains(Cert))
    - of
    - true ->
    - {ok, From,
    - <<"Success">>};
    - false ->
    - {error, From,
    - <<"Certificate host name mismatch">>}
    - end
    - end;
    - CertVerifyRes ->
    - {error, From,
    - p1_tls:get_cert_verify_string(CertVerifyRes,
    - Cert)}
    - end;
    - error ->
    - {error, From,
    - <<"Cannot get peer certificate">>}
    - end;
    + {Result, Message} =
    + ejabberd_s2s:check_peer_certificate(StateData#state.sockmod,
    + StateData#state.socket,
    + From),
    + {Result, From, Message};
    _ ->
    {error, <<"(unknown)">>,
    <<"Got no valid 'from' attribute">>}
    diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
    index a0a8363..9977fcd 100644
    --- a/src/ejabberd_s2s_out.erl
    +++ b/src/ejabberd_s2s_out.erl
    @@ -345,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);