summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Sebastien Pedron <jean-sebastien@rabbitmq.com>2014-12-26 15:13:52 +0100
committerJean-Sebastien Pedron <jean-sebastien@rabbitmq.com>2014-12-26 15:13:52 +0100
commit9a6a61d3847c9a25af4252337e64528386d0bf73 (patch)
tree9994de81fc9a57e62f6d65adcf99c05bcf3a3277
parent4ccccceaaa0a41532ebc15b7572b729af09d2834 (diff)
downloadrabbitmq-server-git-9a6a61d3847c9a25af4252337e64528386d0bf73.tar.gz
Rework SSL's verify_fun support
Until now, only the legacy form was supported (the one existing in Erlang up-to R13B). The user would use the following RabbitMQ configuration: {ssl_options, [ {verify_fun, {Module, Function}} ]} This is converted to the following ssl option: {ssl_options, [ {verify_fun, fun(Errors) -> Module:Function(Errors) end} ]} Although this form is still supported by Erlang R14B+, this is undocumented and some users complain about RabbitMQ not matching the expected behaviour, according to ssl's documentation. Now, the new form is supported as well. Here's what the user would configure: {ssl_options, [ {verify_fun, {Module, Function, InitialUserState}} ]} or: {ssl_options, [ {verify_fun, {Module, Function}} ]} This is converted to the new form: {ssl_options, [ {verify_fun, {fun(OtpCert, Event, State) -> Module:Function(OtpCert, Event, State) end, InitialUserState }} ]} To determine which form to use, we look at the version of the ssl application: o 4.0.1+: We check if Module:Function/3 is exported and use the new form. If Module:Function/1 is exported, we use the old form. If both are exported, the new form is preferred. If InitialUserState is unspecified, 'none' is used. o Before 4.0.1: We use the legacy form only. If Module can't be loaded or if the expected Function/Arity is not exported, an error is logged and RabbitMQ fails to start.
-rw-r--r--src/rabbit_networking.erl59
1 files changed, 55 insertions, 4 deletions
diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl
index d59b22f6d2..1a2883748f 100644
--- a/src/rabbit_networking.erl
+++ b/src/rabbit_networking.erl
@@ -188,12 +188,21 @@ fix_ssl_options(Config) ->
fix_verify_fun(fix_ssl_protocol_versions(Config)).
fix_verify_fun(SslOptsConfig) ->
+ %% Starting with ssl 4.0.1 in Erlang R14B, the verify_fun function
+ %% takes 3 arguments and returns a tuple.
+ {ok, SslAppVer} = application:get_key(ssl, vsn),
+ UseNewVerifyFun = rabbit_misc:version_compare(SslAppVer, "4.0.1", gte),
case rabbit_misc:pget(verify_fun, SslOptsConfig) of
+ {Module, Function, InitialUserState} ->
+ Fun = make_verify_fun(Module, Function, InitialUserState,
+ UseNewVerifyFun),
+ rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
{Module, Function} ->
- rabbit_misc:pset(verify_fun,
- fun (ErrorList) ->
- Module:Function(ErrorList)
- end, SslOptsConfig);
+ Fun = make_verify_fun(Module, Function, none,
+ UseNewVerifyFun),
+ rabbit_misc:pset(verify_fun, Fun, SslOptsConfig);
+ undefined when UseNewVerifyFun ->
+ SslOptsConfig;
undefined ->
% unknown_ca errors are silently ignored prior to R14B unless we
% supply this verify_fun - remove when at least R14B is required
@@ -206,6 +215,48 @@ fix_verify_fun(SslOptsConfig) ->
end
end.
+make_verify_fun(Module, Function, InitialUserState, UseNewVerifyFun) ->
+ try
+ %% Preload the module: it is required to use
+ %% erlang:function_exported/3.
+ Module:module_info()
+ catch
+ _:Exception ->
+ rabbit_log:error("SSL verify_fun: module ~s missing: ~p~n",
+ [Module, Exception]),
+ throw({error, {invalid_verify_fun, missing_module}})
+ end,
+ NewForm = erlang:function_exported(Module, Function, 3),
+ OldForm = erlang:function_exported(Module, Function, 1),
+ case {NewForm, OldForm} of
+ {true, _} when UseNewVerifyFun ->
+ %% This verify_fun is supported by Erlang R14B+ (ssl
+ %% 4.0.1 and later).
+ Fun = fun(OtpCert, Event, UserState) ->
+ Module:Function(OtpCert, Event, UserState)
+ end,
+ {Fun, InitialUserState};
+ {_, true} ->
+ %% This verify_fun is supported by:
+ %% o Erlang up-to R13B;
+ %% o Erlang R14B+ for undocumented backward
+ %% compatibility.
+ %%
+ %% InitialUserState is ignored in this case.
+ fun(ErrorList) ->
+ Module:Function(ErrorList)
+ end;
+ {_, false} when not UseNewVerifyFun ->
+ rabbit_log:error("SSL verify_fun: ~s:~s/1 form required "
+ "for Erlang R13B~n", [Module, Function]),
+ throw({error, {invalid_verify_fun, old_form_required}});
+ _ ->
+ Arity = case UseNewVerifyFun of true -> 3; _ -> 1 end,
+ rabbit_log:error("SSL verify_fun: no ~s:~s/~b exported~n",
+ [Module, Function, Arity]),
+ throw({error, {invalid_verify_fun, function_not_exported}})
+ end.
+
fix_ssl_protocol_versions(Config) ->
case application:get_env(rabbit, ssl_allow_poodle_attack) of
{ok, true} ->