possible incorrect type handling rabbit_auth_mechanism_ssl

184 views
Skip to first unread message

Thibault Cools

unread,
Apr 14, 2021, 1:38:52 PM4/14/21
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&#x2026;

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.

Reply all
Reply to author
Forward
0 new messages