summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2010-12-11 02:28:50 +0100
committerBadlop <badlop@process-one.net>2010-12-11 02:29:53 +0100
commitb9bbe19d4ce697fd500f5869609752eebd278cb9 (patch)
tree7af825b50f8280d77a45c1e31c10118ec47568ac /src
parentInclude From attribute in the stream header of outgoing S2S connections (diff)
Option to reject S2S connection if untrusted certificate (EJAB-464)
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd.cfg.example2
-rw-r--r--src/ejabberd_s2s_in.erl52
-rw-r--r--src/ejabberd_s2s_out.erl2
-rw-r--r--src/tls/tls.erl51
-rw-r--r--src/tls/tls_drv.c9
5 files changed, 93 insertions, 23 deletions
diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example
index 45cdfb4d..a95689b3 100644
--- a/src/ejabberd.cfg.example
+++ b/src/ejabberd.cfg.example
@@ -170,7 +170,7 @@
%%
%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
-%% Allowed values are: false optional required
+%% Allowed values are: false optional required required_trusted
%% You must specify a certificate file.
%%
%%{s2s_use_starttls, optional}.
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 7bc183aa..eb8c05a6 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -75,6 +75,7 @@
tls = false,
tls_enabled = false,
tls_required = false,
+ tls_certverify = false,
tls_options = [],
server,
authenticated = false,
@@ -152,13 +153,15 @@ init([{SockMod, Socket}, Opts]) ->
{value, {_, S}} -> S;
_ -> none
end,
- {StartTLS, TLSRequired} = case ejabberd_config:get_local_option(s2s_use_starttls) of
+ {StartTLS, TLSRequired, TLSCertverify} = case ejabberd_config:get_local_option(s2s_use_starttls) of
UseTls when (UseTls==undefined) or (UseTls==false) ->
- {false, false};
+ {false, false, false};
UseTls when (UseTls==true) or (UseTls==optional) ->
- {true, false};
+ {true, false, false};
required ->
- {true, true}
+ {true, true, false};
+ required_trusted ->
+ {true, true, true}
end,
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
undefined ->
@@ -175,6 +178,7 @@ init([{SockMod, Socket}, Opts]) ->
tls = StartTLS,
tls_enabled = false,
tls_required = TLSRequired,
+ tls_certverify = TLSCertverify,
tls_options = TLSOpts,
timer = Timer}}.
@@ -198,16 +202,18 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
StateData#state.tls_enabled ->
case (StateData#state.sockmod):get_peer_certificate(
StateData#state.socket) of
- {ok, _Cert} ->
- case (StateData#state.sockmod):get_verify_result(
- StateData#state.socket) of
+ {ok, Cert} ->
+ case (StateData#state.sockmod):get_verify_result(StateData#state.socket) of
0 ->
[{xmlelement, "mechanisms",
[{"xmlns", ?NS_SASL}],
[{xmlelement, "mechanism", [],
[{xmlcdata, "EXTERNAL"}]}]}];
- _ ->
- []
+ CertVerifyRes ->
+ case StateData#state.tls_certverify of
+ true -> {error_cert_verif, CertVerifyRes, Cert};
+ false -> []
+ end
end;
error ->
[]
@@ -225,14 +231,26 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
[{xmlelement, "required", [], []}]
}]
end,
- send_element(StateData,
- {xmlelement, "stream:features", [],
- SASL ++ StartTLS ++
- ejabberd_hooks:run_fold(
- s2s_stream_features,
- Server,
- [], [Server])}),
- {next_state, wait_for_feature_request, StateData#state{server = Server}};
+ case SASL of
+ {error_cert_verif, CertVerifyResult, Certificate} ->
+ CertError = tls:get_cert_verify_string(CertVerifyResult, Certificate),
+ RemoteServer = xml:get_attr_s("from", Attrs),
+ ?INFO_MSG("Closing s2s connection: ~s <--> ~s (~s)", [StateData#state.server, RemoteServer, CertError]),
+ send_text(StateData, xml:element_to_string(?SERRT_POLICY_VIOLATION("en", CertError))),
+ {atomic, Pid} = ejabberd_s2s:find_connection(jlib:make_jid("", Server, ""), jlib:make_jid("", RemoteServer, "")),
+ ejabberd_s2s_out:stop_connection(Pid),
+
+ {stop, normal, StateData};
+ _ ->
+ send_element(StateData,
+ {xmlelement, "stream:features", [],
+ SASL ++ StartTLS ++
+ ejabberd_hooks:run_fold(
+ s2s_stream_features,
+ Server,
+ [], [Server])}),
+ {next_state, wait_for_feature_request, StateData#state{server = Server}}
+ end;
{"jabber:server", _, Server, true} when
StateData#state.authenticated ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index 00e7fa1a..f59e8ec8 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -160,7 +160,7 @@ init([From, Server, Type]) ->
{false, false};
UseTls when (UseTls==true) or (UseTls==optional) ->
{true, false};
- required ->
+ UseTls when (UseTls==required) or (UseTls==required_trusted) ->
{true, true}
end,
UseV10 = TLS,
diff --git a/src/tls/tls.erl b/src/tls/tls.erl
index 46f01b36..7c90569c 100644
--- a/src/tls/tls.erl
+++ b/src/tls/tls.erl
@@ -39,6 +39,7 @@
close/1,
get_peer_certificate/1,
get_verify_result/1,
+ get_cert_verify_string/2,
test/0]).
%% Internal exports, call-back functions.
@@ -63,8 +64,10 @@
-ifdef(SSL40).
-define(CERT_DECODE, {public_key, pkix_decode_cert, plain}).
+-define(CERT_SELFSIGNED, {public_key, pkix_is_self_signed}).
-else.
-define(CERT_DECODE, {ssl_pkix, decode_cert, [pkix]}).
+-define(CERT_SELFSIGNED, {erlang, is_atom}). %% Dummy function for old OTPs
-endif.
@@ -243,7 +246,9 @@ get_peer_certificate(#tlssock{tlsport = Port}) ->
<<0, BCert/binary>> ->
{CertMod, CertFun, CertSecondArg} = ?CERT_DECODE,
case catch apply(CertMod, CertFun, [BCert, CertSecondArg]) of
- {ok, Cert} ->
+ {ok, Cert} -> %% returned by R13 and older
+ {ok, Cert};
+ {'Certificate', _, _, _} = Cert ->
{ok, Cert};
_ ->
error
@@ -311,3 +316,47 @@ loop(Port, Socket) ->
end.
+get_cert_verify_string(CertVerifyRes, Cert) ->
+ BCert = public_key:pkix_encode('Certificate', Cert, plain),
+ {CertMod, CertFun} = ?CERT_SELFSIGNED,
+ IsSelfsigned = apply(CertMod, CertFun, [BCert]),
+ case {CertVerifyRes, IsSelfsigned} of
+ {21, true} -> "self-signed certificate";
+ _ -> cert_verify_code(CertVerifyRes)
+ end.
+
+%% http://www.openssl.org/docs/apps/verify.html
+cert_verify_code(0) -> "ok";
+cert_verify_code(2) -> "unable to get issuer certificate";
+cert_verify_code(3) -> "unable to get certificate CRL";
+cert_verify_code(4) -> "unable to decrypt certificate's signature";
+cert_verify_code(5) -> "unable to decrypt CRL's signature";
+cert_verify_code(6) -> "unable to decode issuer public key";
+cert_verify_code(7) -> "certificate signature failure";
+cert_verify_code(8) -> "CRL signature failure";
+cert_verify_code(9) -> "certificate is not yet valid";
+cert_verify_code(10) -> "certificate has expired";
+cert_verify_code(11) -> "CRL is not yet valid";
+cert_verify_code(12) -> "CRL has expired";
+cert_verify_code(13) -> "format error in certificate's notBefore field";
+cert_verify_code(14) -> "format error in certificate's notAfter field";
+cert_verify_code(15) -> "format error in CRL's lastUpdate field";
+cert_verify_code(16) -> "format error in CRL's nextUpdate field";
+cert_verify_code(17) -> "out of memory";
+cert_verify_code(18) -> "self signed certificate";
+cert_verify_code(19) -> "self signed certificate in certificate chain";
+cert_verify_code(20) -> "unable to get local issuer certificate";
+cert_verify_code(21) -> "unable to verify the first certificate";
+cert_verify_code(22) -> "certificate chain too long";
+cert_verify_code(23) -> "certificate revoked";
+cert_verify_code(24) -> "invalid CA certificate";
+cert_verify_code(25) -> "path length constraint exceeded";
+cert_verify_code(26) -> "unsupported certificate purpose";
+cert_verify_code(27) -> "certificate not trusted";
+cert_verify_code(28) -> "certificate rejected";
+cert_verify_code(29) -> "subject issuer mismatch";
+cert_verify_code(30) -> "authority and subject key identifier mismatch";
+cert_verify_code(31) -> "authority and issuer serial number mismatch";
+cert_verify_code(32) -> "key usage does not include certificate signing";
+cert_verify_code(50) -> "application verification failure";
+cert_verify_code(X) -> "Unknown OpenSSL error code: " ++ integer_to_list(X).
diff --git a/src/tls/tls_drv.c b/src/tls/tls_drv.c
index fd4e7fff..ae870762 100644
--- a/src/tls/tls_drv.c
+++ b/src/tls/tls_drv.c
@@ -349,13 +349,16 @@ static int tls_drv_control(ErlDrvData handle,
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
+ /* SSL_CTX_load_verify_locations(ctx, "/etc/ejabberd/ca_certificates.pem", NULL); */
+ /* SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ejabberd/ca_certs/"); */
- if (command == SET_CERTIFICATE_FILE_ACCEPT)
- {
+ /* This IF is commented to allow verification in all cases: */
+ /* if (command == SET_CERTIFICATE_FILE_ACCEPT) */
+ /* { */
SSL_CTX_set_verify(ctx,
SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
verify_callback);
- }
+ /* } */
ssl_ctx = ctx;
hash_table_insert(buf, mtime, ssl_ctx);