Thibault Cools
unread,Apr 14, 2021, 1:38:52 PM4/14/21Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to rabbitmq-users
Hello,
I wonder if I either have found a bug or am misunderstanding the
workings of `rabbit_auth_mechanism_ssl`.
The goal is to authenticate clients by using certificates. The
certificates use SAN OtherName to store the username for rabbitmq.
The config I use for this, is as follows:
auth_mechanisms.1 = EXTERNAL
ssl_cert_login_from = subject_alternative_name
ssl_cert_login_san_type = other_name
ssl_cert_login_san_index =0
RabbitMQ-server has one user with username `trein` that corresponds to the
value of `other-name` in the certificate:
$ openssl asn1parse -in cert.pem -strparse 614
0:d=0 hl=2 l= 34 cons: SEQUENCE
2:d=1 hl=2 l= 19 cons: cont [ 0 ]
4:d=2 hl=2 l= 8 prim: OBJECT <redacted oid>
14:d=2 hl=2 l= 7 cons: cont [ 0 ]
16:d=3 hl=2 l= 5 prim: UTF8STRING :trein
23:d=1 hl=2 l= 11 prim: cont [ 2 ]
Upon connecting with the server, it is possible to start a TLS
connection, but then RabbitMQ authentication happens and it results
in the following error:
{handshake_error,starting,0,{error,badarg,'connection.start_ok',[{io_lib,format,["user '~s' - invalid credentials",[{'AnotherName',{<redacted oid>},<<12,5,116,114,101,105,110>>}]]...
The redacted oid above(and everywhere else further down) is a list of
numbers like 1, 2, 3, 4, 5…
I have no knowledge of Erlang, but from what I've found I see that the
username is not represented as a string in this case but as a
record(or tuple?) instead.
After diving into the code I found following relevant pieces of code:
This is where the error happens:
init(Sock) ->
Username = case rabbit_net:peercert(Sock) of
{ok, C} ->
case rabbit_ssl:peer_cert_auth_name(C) of
unsafe -> {refused, none, "TLS configuration is unsafe", []};
not_found -> {refused, none, "no name found", []};
Name -> rabbit_data_coercion:to_binary(Name)
end;
{error, no_peercert} ->
{refused, none, "connection peer presented no TLS (x.509) certificate", []};
nossl ->
{refused, none, "not a TLS-enabled connection", []}
end,
% error from here
rabbit_log:debug("auth mechanism TLS extracted username '~s' from peer certificate", [Username]),
#state{username = Username}.
From this, together with the definition of `to_binary` I assume that
`peer_cert_auth_name` doesn't return a value with the correct type.
`Username` should be a string(or something with similar behaviour like
a list), while in this case it is tuple or record
`{'AnotherName',...}`
Looking at `peer_cert_auth_name`, which might be the one return a
wrong value:
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;
I notice `OfType` is like the following:
[{otherName,{'AnotherName',{<redacted oid>},<<12,5,116,114,101,105,110>>}}]
This line is probably the important one:
{_, Value} = lists:nth(Index, OfType),
I think this means that `OfType` is a list of records or tuples that
look like `{A, B}`, and this takes `B` from the nth element. In this
case it takes `{'AnotherName',{<redacted
oid>},<<12,5,116,114,101,105,110>>}`, but I think this should
eventually take only `<<12,5,116,114,101,105,110>>`, which is the
username "trein".
When I changed the current setup to put the username in `uri` SAN
instead of `other_name`, I did not have this issue and the `OfType`
was like `{uniformResourceIdentifier, "trein"}`. This makes me think
that:
1. every other type of SAN(ip, email, dns) is also like `{recordType, value}` where
value is a string-like type, and
2. `{otherName, {'AnotherName', {oid}, value}}` is of a different type, and should
therefor be handled differently.
I am wondering whether my assumptions are correct or if I am making
mistakes when it comes to using `other_name` as username for
authentication.
Let me know if more information is needed.