fix eldap certificate verification (#3528)

Reported in #3527. Add hostname matching function, and specify SNI

Also, OTP 23 dropped backwards compatibility for 0, 1, 2 values for verify, so
replace with combination of verify_none/verify_peer and fail_if_no_peer_cert
as appropriate
This commit is contained in:
Stu Tomlinson 2021-02-15 13:29:58 +00:00 committed by GitHub
parent e3fd120fd4
commit 1f194e417d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 5 deletions

View File

@ -96,6 +96,7 @@
{if_version_above, "20", {d, 'DEPRECATED_GET_STACKTRACE'}},
{if_version_below, "21", {d, 'USE_OLD_HTTP_URI'}},
{if_version_below, "22", {d, 'LAGER'}},
{if_version_below, "21", {d, 'NO_CUSTOMIZE_HOSTNAME_CHECK'}},
{if_version_below, "23", {d, 'USE_OLD_CRYPTO_HMAC'}},
{if_version_below, "23", {d, 'USE_OLD_PG2'}},
{if_var_match, db_type, mssql, {d, 'mssql'}},

View File

@ -132,7 +132,8 @@
tls_options = [] :: [{certfile, string()} |
{cacertfile, string()} |
{depth, non_neg_integer()} |
{verify, non_neg_integer()}],
{verify, non_neg_integer()} |
{fail_if_no_peer_cert, boolean()}],
fd :: gen_tcp:socket() | undefined,
rootdn = <<"">> :: binary(),
passwd = <<"">> :: binary(),
@ -604,9 +605,9 @@ init([Hosts, Port, Rootdn, Passwd, Opts]) ->
[]),
CertOpts;
Verify == soft ->
[{verify, 1}] ++ CertOpts ++ CacertOpts ++ DepthOpts;
[{verify, verify_peer}, {fail_if_no_peer_cert, false}] ++ CertOpts ++ CacertOpts ++ DepthOpts;
Verify == hard ->
[{verify, 2}] ++ CertOpts ++ CacertOpts ++ DepthOpts;
[{verify, verify_peer}, {fail_if_no_peer_cert, true}] ++ CertOpts ++ CacertOpts ++ DepthOpts;
true -> []
end,
{ok, connecting,
@ -1035,22 +1036,40 @@ polish([H | T], Res,
polish(T, Res, [H | Ref]);
polish([], Res, Ref) -> {Res, Ref}.
-ifdef(NO_CUSTOMIZE_HOSTNAME_CHECK).
check_hostname_opt(TLSOpts) ->
TLSOpts.
-else.
check_hostname_opt(TLSOpts) ->
MatchFun = public_key:pkix_verify_hostname_match_fun(https),
[{customize_hostname_check, [{match_fun, MatchFun}]} | TLSOpts].
-endif.
host_tls_options(Host, TLSOpts) ->
case proplists:get_value(verify, TLSOpts) of
verify_peer ->
check_hostname_opt([{server_name_indication, Host} | TLSOpts]);
_ ->
TLSOpts
end.
%%-----------------------------------------------------------------------
%% Connect to next server in list and attempt to bind to it.
%%-----------------------------------------------------------------------
connect_bind(S) ->
Host = next_host(S#eldap.host, S#eldap.hosts),
HostS = binary_to_list(Host),
Opts = if S#eldap.tls == tls ->
[{packet, asn1}, {active, true}, {keepalive, true},
binary
| S#eldap.tls_options];
| host_tls_options(HostS, S#eldap.tls_options)];
true ->
[{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary]
end,
?DEBUG("Connecting to LDAP server at ~ts:~p with options ~p",
[Host, S#eldap.port, Opts]),
HostS = binary_to_list(Host),
SockMod = case S#eldap.tls of
tls -> ssl;
_ -> gen_tcp