diff options
author | Jean-Sebastien Pedron <jean-sebastien@rabbitmq.com> | 2014-12-26 15:13:52 +0100 |
---|---|---|
committer | Jean-Sebastien Pedron <jean-sebastien@rabbitmq.com> | 2014-12-26 15:13:52 +0100 |
commit | d40e75a4bc4a9020765acf83f5b1b87e533c5fd1 (patch) | |
tree | 9994de81fc9a57e62f6d65adcf99c05bcf3a3277 | |
parent | 1aee5daf34be8b6744fab65722a62e93c286960f (diff) | |
download | rabbitmq-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.erl | 59 |
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} -> |