not StateData#state.authenticated ->
send_text(StateData,
?STREAM_HEADER(<<" version='1.0'">>)),
- SASL = if StateData#state.tls_enabled ->
- case
- (StateData#state.sockmod):get_peer_certificate(StateData#state.socket)
- of
- {ok, Cert} ->
+ 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_verify_result(StateData#state.socket)
+ (StateData#state.sockmod):get_peer_certificate(StateData#state.socket)
of
- 0 ->
- [#xmlel{name = <<"mechanisms">>,
- attrs = [{<<"xmlns">>, ?NS_SASL}],
- children =
- [#xmlel{name = <<"mechanism">>,
- attrs = [],
- children =
- [{xmlcdata,
- <<"EXTERNAL">>}]}]}];
- CertVerifyRes ->
- case StateData#state.tls_certverify of
- true ->
- {error_cert_verif, CertVerifyRes,
- Cert};
- false -> []
- end
+ {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;
- error -> []
+ _ ->
+ {error, <<"(unknown)">>,
+ <<"Got no valid 'from' attribute">>}
end;
- true -> []
+ true ->
+ {no_verify, <<"(unknown)">>,
+ <<"TLS not (yet) enabled">>}
end,
StartTLS = if StateData#state.tls_enabled -> [];
not StateData#state.tls_enabled and
[#xmlel{name = <<"required">>,
attrs = [], children = []}]}]
end,
- case SASL of
- {error_cert_verif, CertVerifyResult, Certificate} ->
- CertError = p1_tls:get_cert_verify_string(CertVerifyResult,
- Certificate),
- RemoteServer = xml:get_attr_s(<<"from">>, Attrs),
+ case Auth of
+ {error, RemoteServer, CertError}
+ when StateData#state.tls_certverify ->
?INFO_MSG("Closing s2s connection: ~s <--> ~s (~s)",
[StateData#state.server, RemoteServer, CertError]),
send_text(StateData,
<<"">>)),
ejabberd_s2s_out:stop_connection(Pid),
{stop, normal, StateData};
- _ ->
- send_element(StateData,
+ {VerifyResult, RemoteServer, Msg} ->
+ {SASL, NewStateData} = case VerifyResult of
+ ok ->
+ {[#xmlel{name = <<"mechanisms">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = <<"mechanism">>,
+ attrs = [],
+ children =
+ [{xmlcdata,
+ <<"EXTERNAL">>}]}]}],
+ StateData#state{auth_domain = RemoteServer}};
+ error ->
+ ?DEBUG("Won't accept certificate of ~s: ~s",
+ [RemoteServer, Msg]),
+ {[], StateData};
+ no_verify ->
+ {[], StateData}
+ end,
+ send_element(NewStateData,
#xmlel{name = <<"stream:features">>, attrs = [],
children =
SASL ++
Server, [],
[Server])}),
{next_state, wait_for_feature_request,
- StateData#state{server = Server}}
+ NewStateData#state{server = Server}}
end;
{<<"jabber:server">>, _, Server, true}
when StateData#state.authenticated ->
wait_for_feature_request({xmlstreamelement, El},
StateData) ->
- #xmlel{name = Name, attrs = Attrs, children = Els} = El,
+ #xmlel{name = Name, attrs = Attrs} = El,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
SockMod =
{?NS_SASL, <<"auth">>} when TLSEnabled ->
Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
case Mech of
- <<"EXTERNAL">> ->
- Auth = jlib:decode_base64(xml:get_cdata(Els)),
- AuthDomain = jlib:nameprep(Auth),
- AuthRes = 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 AuthDomain of
- error -> false;
- _ ->
- case
- idna:domain_utf8_to_ascii(AuthDomain)
- of
- false -> false;
- PCAuthDomain ->
- lists:any(fun (D) ->
- match_domain(PCAuthDomain,
- D)
- end,
- get_cert_domains(Cert))
- end
- end;
- _ -> false
- end;
- error -> false
- end,
+ <<"EXTERNAL">> when StateData#state.auth_domain /= <<"">> ->
+ AuthDomain = StateData#state.auth_domain,
AllowRemoteHost = ejabberd_s2s:allow_host(<<"">>,
AuthDomain),
- if AuthRes andalso AllowRemoteHost ->
+ if AllowRemoteHost ->
(StateData#state.sockmod):reset_stream(StateData#state.socket),
send_element(StateData,
#xmlel{name = <<"success">>,
jlib:make_jid(<<"">>, AuthDomain, <<"">>)),
{next_state, wait_for_stream,
StateData#state{streamid = new_id(),
- authenticated = true,
- auth_domain = AuthDomain}};
+ authenticated = true}};
true ->
send_element(StateData,
#xmlel{name = <<"failure">>,