summaryrefslogtreecommitdiff
path: root/deps/rabbit/src/rabbit_ssl.erl
diff options
context:
space:
mode:
Diffstat (limited to 'deps/rabbit/src/rabbit_ssl.erl')
-rw-r--r--deps/rabbit/src/rabbit_ssl.erl195
1 files changed, 195 insertions, 0 deletions
diff --git a/deps/rabbit/src/rabbit_ssl.erl b/deps/rabbit/src/rabbit_ssl.erl
new file mode 100644
index 0000000000..84670b0a19
--- /dev/null
+++ b/deps/rabbit/src/rabbit_ssl.erl
@@ -0,0 +1,195 @@
+%% This Source Code Form is subject to the terms of the Mozilla Public
+%% License, v. 2.0. If a copy of the MPL was not distributed with this
+%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+%%
+%% Copyright (c) 2007-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(rabbit_ssl).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-export([peer_cert_issuer/1, peer_cert_subject/1, peer_cert_validity/1]).
+-export([peer_cert_subject_items/2, peer_cert_auth_name/1]).
+-export([cipher_suites_erlang/2, cipher_suites_erlang/1,
+ cipher_suites_openssl/2, cipher_suites_openssl/1,
+ cipher_suites/1]).
+
+%%--------------------------------------------------------------------------
+
+-export_type([certificate/0]).
+
+% Due to API differences between OTP releases.
+-dialyzer(no_missing_calls).
+-ignore_xref([{ssl_cipher_format, suite_legacy, 1},
+ {ssl_cipher_format, suite, 1},
+ {ssl_cipher_format, suite_to_str, 1},
+ {ssl_cipher_format, erl_suite_definition, 1},
+ {ssl_cipher_format, suite_map_to_openssl_str, 1},
+ {ssl_cipher_format, suite_map_to_bin, 1}]).
+
+-type certificate() :: rabbit_cert_info:certificate().
+
+-type cipher_suites_mode() :: default | all | anonymous.
+
+-spec cipher_suites(cipher_suites_mode()) -> ssl:ciphers().
+cipher_suites(Mode) ->
+ Version = get_highest_protocol_version(),
+ ssl:cipher_suites(Mode, Version).
+
+-spec cipher_suites_erlang(cipher_suites_mode()) ->
+ [ssl:old_cipher_suite()].
+cipher_suites_erlang(Mode) ->
+ Version = get_highest_protocol_version(),
+ cipher_suites_erlang(Mode, Version).
+
+-spec cipher_suites_erlang(cipher_suites_mode(),
+ ssl:protocol_version() | tls_record:tls_version()) ->
+ [ssl:old_cipher_suite()].
+cipher_suites_erlang(Mode, Version) ->
+ [ format_cipher_erlang(C)
+ || C <- ssl:cipher_suites(Mode, Version) ].
+
+-spec cipher_suites_openssl(cipher_suites_mode()) ->
+ [ssl:old_cipher_suite()].
+cipher_suites_openssl(Mode) ->
+ Version = get_highest_protocol_version(),
+ cipher_suites_openssl(Mode, Version).
+
+-spec cipher_suites_openssl(cipher_suites_mode(),
+ ssl:protocol_version() | tls_record:tls_version()) ->
+ [ssl:old_cipher_suite()].
+cipher_suites_openssl(Mode, Version) ->
+ lists:filtermap(fun(C) ->
+ OpenSSL = format_cipher_openssl(C),
+ case is_list(OpenSSL) of
+ true -> {true, OpenSSL};
+ false -> false
+ end
+ end,
+ ssl:cipher_suites(Mode, Version)).
+
+
+format_cipher_erlang(Cipher) ->
+ case erlang:function_exported(ssl_cipher_format, suite_map_to_bin, 1) of
+ true ->
+ format_cipher_erlang22(Cipher);
+ false ->
+ format_cipher_erlang21(Cipher)
+ end.
+
+format_cipher_erlang22(Cipher) ->
+ ssl_cipher_format:suite_legacy(ssl_cipher_format:suite_map_to_bin(Cipher)).
+
+format_cipher_erlang21(Cipher) ->
+ ssl_cipher_format:erl_suite_definition(ssl_cipher_format:suite(Cipher)).
+
+
+format_cipher_openssl(Cipher) ->
+ case erlang:function_exported(ssl_cipher_format, suite_map_to_bin, 1) of
+ true ->
+ format_cipher_openssl22(Cipher);
+ false ->
+ format_cipher_openssl21(Cipher)
+ end.
+
+format_cipher_openssl22(Cipher) ->
+ ssl_cipher_format:suite_map_to_openssl_str(Cipher).
+
+format_cipher_openssl21(Cipher) ->
+ ssl_cipher_format:suite_to_str(Cipher).
+
+-spec get_highest_protocol_version() -> tls_record:tls_atom_version().
+get_highest_protocol_version() ->
+ tls_record:protocol_version(
+ tls_record:highest_protocol_version([])).
+
+%%--------------------------------------------------------------------------
+%% High-level functions used by reader
+%%--------------------------------------------------------------------------
+
+%% Return a string describing the certificate's issuer.
+peer_cert_issuer(Cert) ->
+ rabbit_cert_info:issuer(Cert).
+
+%% Return a string describing the certificate's subject, as per RFC4514.
+peer_cert_subject(Cert) ->
+ rabbit_cert_info:subject(Cert).
+
+%% Return the parts of the certificate's subject.
+peer_cert_subject_items(Cert, Type) ->
+ rabbit_cert_info:subject_items(Cert, Type).
+
+%% Filters certificate SAN extensions by (OTP) SAN type name.
+peer_cert_subject_alternative_names(Cert, Type) ->
+ SANs = rabbit_cert_info:subject_alternative_names(Cert),
+ lists:filter(fun({Key, _}) -> Key =:= Type end, SANs).
+
+%% Return a string describing the certificate's validity.
+peer_cert_validity(Cert) ->
+ rabbit_cert_info:validity(Cert).
+
+%% Extract a username from the certificate
+-spec peer_cert_auth_name
+ (certificate()) -> binary() | 'not_found' | 'unsafe'.
+
+peer_cert_auth_name(Cert) ->
+ {ok, Mode} = application:get_env(rabbit, ssl_cert_login_from),
+ peer_cert_auth_name(Mode, Cert).
+
+peer_cert_auth_name(distinguished_name, Cert) ->
+ case auth_config_sane() of
+ true -> iolist_to_binary(peer_cert_subject(Cert));
+ false -> unsafe
+ end;
+
+peer_cert_auth_name(subject_alt_name, Cert) ->
+ peer_cert_auth_name(subject_alternative_name, Cert);
+
+peer_cert_auth_name(subject_alternative_name, Cert) ->
+ case auth_config_sane() of
+ true ->
+ Type = application:get_env(rabbit, ssl_cert_login_san_type, dns),
+ %% lists:nth/2 is 1-based
+ Index = application:get_env(rabbit, ssl_cert_login_san_index, 0) + 1,
+ OfType = peer_cert_subject_alternative_names(Cert, otp_san_type(Type)),
+ rabbit_log:debug("Peer certificate SANs of type ~s: ~p, index to use with lists:nth/2: ~b", [Type, OfType, Index]),
+ case length(OfType) of
+ 0 -> not_found;
+ N when N < Index -> not_found;
+ N when N >= Index ->
+ {_, Value} = lists:nth(Index, OfType),
+ rabbit_data_coercion:to_binary(Value)
+ end;
+ false -> unsafe
+ end;
+
+peer_cert_auth_name(common_name, Cert) ->
+ %% If there is more than one CN then we join them with "," in a
+ %% vaguely DN-like way. But this is more just so we do something
+ %% more intelligent than crashing, if you actually want to escape
+ %% things properly etc, use DN mode.
+ case auth_config_sane() of
+ true -> case peer_cert_subject_items(Cert, ?'id-at-commonName') of
+ not_found -> not_found;
+ CNs -> list_to_binary(string:join(CNs, ","))
+ end;
+ false -> unsafe
+ end.
+
+auth_config_sane() ->
+ {ok, Opts} = application:get_env(rabbit, ssl_options),
+ case proplists:get_value(verify, Opts) of
+ verify_peer -> true;
+ V -> rabbit_log:warning("TLS peer verification (authentication) is "
+ "disabled, ssl_options.verify value used: ~p. "
+ "See https://www.rabbitmq.com/ssl.html#peer-verification to learn more.", [V]),
+ false
+ end.
+
+otp_san_type(dns) -> dNSName;
+otp_san_type(ip) -> iPAddress;
+otp_san_type(email) -> rfc822Name;
+otp_san_type(uri) -> uniformResourceIdentifier;
+otp_san_type(other_name) -> otherName;
+otp_san_type(Other) -> Other.