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
commitd40e75a4bc4a9020765acf83f5b1b87e533c5fd1 (patch)
tree9994de81fc9a57e62f6d65adcf99c05bcf3a3277
parent1aee5daf34be8b6744fab65722a62e93c286960f (diff)
downloadrabbitmq-server-bug26503.tar.gz
Rework SSL's verify_fun supportbug26503
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 d59b22f6..1a288374 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} ->