diff options
| -rw-r--r-- | src/rabbit_auth_mechanism.erl | 2 | ||||
| -rw-r--r-- | src/rabbit_auth_mechanism_amqplain.erl | 4 | ||||
| -rw-r--r-- | src/rabbit_auth_mechanism_external.erl | 49 | ||||
| -rw-r--r-- | src/rabbit_auth_mechanism_plain.erl | 4 | ||||
| -rw-r--r-- | src/rabbit_auth_mechanism_scram_md5.erl | 4 | ||||
| -rw-r--r-- | src/rabbit_reader.erl | 2 | ||||
| -rw-r--r-- | src/rabbit_ssl.erl | 24 |
7 files changed, 62 insertions, 27 deletions
diff --git a/src/rabbit_auth_mechanism.erl b/src/rabbit_auth_mechanism.erl index b13a14ec38..c932471d77 100644 --- a/src/rabbit_auth_mechanism.erl +++ b/src/rabbit_auth_mechanism.erl @@ -44,7 +44,7 @@ behaviour_info(callbacks) -> %% Called before authentication starts. Should create a state %% object to be passed through all the stages of authentication. - {init, 0}, + {init, 1}, %% Handle a stage of authentication. Possible responses: %% {ok, User} diff --git a/src/rabbit_auth_mechanism_amqplain.erl b/src/rabbit_auth_mechanism_amqplain.erl index 61f61e403a..0207e6c63b 100644 --- a/src/rabbit_auth_mechanism_amqplain.erl +++ b/src/rabbit_auth_mechanism_amqplain.erl @@ -34,7 +34,7 @@ -behaviour(rabbit_auth_mechanism). --export([description/0, should_offer/1, init/0, handle_response/2]). +-export([description/0, should_offer/1, init/1, handle_response/2]). -include("rabbit_auth_mechanism_spec.hrl"). @@ -56,7 +56,7 @@ description() -> should_offer(_Sock) -> true. -init() -> +init(_Sock) -> []. handle_response(Response, _State) -> diff --git a/src/rabbit_auth_mechanism_external.erl b/src/rabbit_auth_mechanism_external.erl index 251aa41770..43f2a196d6 100644 --- a/src/rabbit_auth_mechanism_external.erl +++ b/src/rabbit_auth_mechanism_external.erl @@ -34,10 +34,12 @@ -behaviour(rabbit_auth_mechanism). --export([description/0, should_offer/1, init/0, handle_response/2]). +-export([description/0, should_offer/1, init/1, handle_response/2]). -include("rabbit_auth_mechanism_spec.hrl"). +-include_lib("public_key/include/public_key.hrl"). + -rabbit_boot_step({?MODULE, [{description, "auth mechanism external"}, {mfa, {rabbit_registry, register, @@ -45,27 +47,42 @@ {requires, rabbit_registry}, {enables, kernel_ready}]}). -%% SASL PLAIN, as used by the Qpid Java client and our clients. Also, -%% apparently, by OpenAMQ. +-record(state, {username = undefined}). + +%% SASL EXTERNAL. SASL says EXTERNAL means "use credentials +%% established by means external to the mechanism". We define that to +%% mean the peer certificate's subject's CN. description() -> [{name, <<"EXTERNAL">>}, {description, <<"SASL EXTERNAL authentication mechanism">>}]. +%% TODO: safety check, don't offer unless verify_peer set should_offer(Sock) -> - Cert = case rabbit_net:peercert(Sock) of - nossl -> 'not_ssl'; - {error, no_peercert} -> 'no_peer_cert'; - {ok, C} -> C - end, + case peer_subject(Sock) of + none -> false; + _ -> true + end. + +init(Sock) -> + {ok, C} = rabbit_net:peercert(Sock), + CN = case rabbit_ssl:peer_cert_subject_item(C, ?'id-at-commonName') of + not_found -> not_found; + CN0 -> list_to_binary(CN0) + end, + #state{username = CN}. - io:format("Sock: ~p~n", [{Sock, Cert}]), - true. +handle_response(_Response, #state{username = Username}) -> + case Username of + not_found -> {refused, Username}; + _ -> rabbit_access_control:lookup_user(Username) + end. -init() -> - []. +%%-------------------------------------------------------------------------- -handle_response(Response, _State) -> - [User, Pass] = [list_to_binary(T) || - T <- string:tokens(binary_to_list(Response), [0])], - rabbit_access_control:check_user_pass_login(User, Pass). +peer_subject(Sock) -> + case rabbit_net:peercert(Sock) of + nossl -> none; + {error, no_peercert} -> none; + {ok, C} -> rabbit_ssl:peer_cert_subject(C) + end. diff --git a/src/rabbit_auth_mechanism_plain.erl b/src/rabbit_auth_mechanism_plain.erl index 28aed92b43..7de6197625 100644 --- a/src/rabbit_auth_mechanism_plain.erl +++ b/src/rabbit_auth_mechanism_plain.erl @@ -34,7 +34,7 @@ -behaviour(rabbit_auth_mechanism). --export([description/0, should_offer/1, init/0, handle_response/2]). +-export([description/0, should_offer/1, init/1, handle_response/2]). -include("rabbit_auth_mechanism_spec.hrl"). @@ -55,7 +55,7 @@ description() -> should_offer(_Sock) -> true. -init() -> +init(_Sock) -> []. handle_response(Response, _State) -> diff --git a/src/rabbit_auth_mechanism_scram_md5.erl b/src/rabbit_auth_mechanism_scram_md5.erl index 02032bdd76..688f9dee30 100644 --- a/src/rabbit_auth_mechanism_scram_md5.erl +++ b/src/rabbit_auth_mechanism_scram_md5.erl @@ -34,7 +34,7 @@ -behaviour(rabbit_auth_mechanism). --export([description/0, should_offer/1, init/0, handle_response/2]). +-export([description/0, should_offer/1, init/1, handle_response/2]). -include("rabbit_auth_mechanism_spec.hrl"). @@ -67,7 +67,7 @@ description() -> should_offer(_Sock) -> true. -init() -> +init(_Sock) -> #state{}. handle_response(Username, State = #state{username = undefined}) -> diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index cc25c83393..dd5bd61d72 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -752,7 +752,7 @@ handle_method0(#'connection.start_ok'{mechanism = Mechanism, sock = Sock}) -> AuthMechanism = auth_mechanism_to_module(Mechanism, Sock), State = State0#v1{auth_mechanism = AuthMechanism, - auth_state = AuthMechanism:init(), + auth_state = AuthMechanism:init(Sock), connection_state = securing, connection = Connection#connection{ diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index 4335dd2e40..d37d77a650 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -36,6 +36,7 @@ -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_item/2]). %%-------------------------------------------------------------------------- @@ -45,9 +46,10 @@ -type(certificate() :: binary()). --spec(peer_cert_issuer/1 :: (certificate()) -> string()). --spec(peer_cert_subject/1 :: (certificate()) -> string()). --spec(peer_cert_validity/1 :: (certificate()) -> string()). +-spec(peer_cert_issuer/1 :: (certificate()) -> string()). +-spec(peer_cert_subject/1 :: (certificate()) -> string()). +-spec(peer_cert_validity/1 :: (certificate()) -> string()). +-spec(peer_cert_subject_item/2 :: (certificate(), atom()) -> string()). -endif. @@ -71,6 +73,14 @@ peer_cert_subject(Cert) -> format_rdn_sequence(Subject) end, Cert). +%% Return a part of the certificate's subject. +peer_cert_subject_item(Cert, Type) -> + cert_info(fun(#'OTPCertificate' { + tbsCertificate = #'OTPTBSCertificate' { + subject = Subject }}) -> + find_by_type(Type, Subject) + end, Cert). + %% Return a string describing the certificate's validity. peer_cert_validity(Cert) -> cert_info(fun(#'OTPCertificate' { @@ -89,6 +99,14 @@ cert_info(F, Cert) -> DecCert -> DecCert end). +find_by_type(Type, {rdnSequence, RDNs}) -> + case [V || #'AttributeTypeAndValue'{type = T, value = V} + <- lists:flatten(RDNs), + T == Type] of + [{printableString, S}] -> S; + [] -> not_found + end. + %%-------------------------------------------------------------------------- %% Formatting functions %%-------------------------------------------------------------------------- |
