Skip to content

Commit b630fc0

Browse files
Merge pull request #16712 from rabbitmq/rabbitmq-server-16101-follow-up
A follow-up to #16101
2 parents 446f76e + 36f636c commit b630fc0

4 files changed

Lines changed: 49 additions & 10 deletions

File tree

deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap.erl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
-export([get_connections/0]).
2626

2727
%% for tests
28-
-export([purge_connections/0]).
28+
-export([purge_connections/0,
29+
fill_user_dn_pattern/1, escaped_user_dn/1]).
2930

3031
-define(L(F, A), log("LDAP " ++ F, A)).
3132
-define(L1(F, A), log(" LDAP " ++ F, A)).
@@ -895,9 +896,7 @@ username_to_dn_prebind(Username) ->
895896
fun (LDAP) -> dn_lookup(Username, LDAP) end).
896897

897898
username_to_dn(Username, LDAP, postbind) -> dn_lookup(Username, LDAP);
898-
username_to_dn(Username, _LDAP, _When) ->
899-
ADArgs = rabbit_auth_backend_ldap_util:get_active_directory_args(Username),
900-
fill_dn(env(user_dn_pattern), [{username, Username}] ++ ADArgs).
899+
username_to_dn(Username, _LDAP, _When) -> escaped_user_dn(Username).
901900

902901
dn_lookup(Username, LDAP) ->
903902
Filled = fill_user_dn_pattern(Username),
@@ -916,7 +915,8 @@ dn_lookup(Username, LDAP) ->
916915
?LOG_WARNING("Searching for DN for ~ts, got back ~tp",
917916
[Filled, Entries],
918917
#{domain => ?RMQLOG_DOMAIN_LDAP}),
919-
Filled;
918+
%% Used as a bind DN, so RFC 4514-escape substituted values.
919+
escaped_user_dn(Username);
920920
{error, _} = E ->
921921
exit(E)
922922
end.
@@ -925,12 +925,17 @@ fill_user_dn_pattern(Username) ->
925925
ADArgs = rabbit_auth_backend_ldap_util:get_active_directory_args(Username),
926926
fill(env(user_dn_pattern), [{username, Username}] ++ ADArgs).
927927

928+
%% The user DN built from `user_dn_pattern' with RFC 4514-escaped
929+
%% substitutions. A no-op for usernames without DN-special characters.
930+
escaped_user_dn(Username) ->
931+
ADArgs = rabbit_auth_backend_ldap_util:get_active_directory_args(Username),
932+
fill_dn(env(user_dn_pattern), [{username, Username}] ++ ADArgs).
933+
928934
simple_bind_fill_pattern(Username) ->
929935
simple_bind_fill_pattern(env(user_bind_pattern), Username).
930936

931937
simple_bind_fill_pattern(none, Username) ->
932-
ADArgs = rabbit_auth_backend_ldap_util:get_active_directory_args(Username),
933-
fill_dn(env(user_dn_pattern), [{username, Username}] ++ ADArgs);
938+
escaped_user_dn(Username);
934939
simple_bind_fill_pattern(Pattern, Username) ->
935940
ADArgs = rabbit_auth_backend_ldap_util:get_active_directory_args(Username),
936941
fill_dn(Pattern, [{username, Username}] ++ ADArgs).

deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap_util.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
-export([fill/2, get_active_directory_args/1, parse_query/1]).
1111

12+
%% Raw substitution without escaping. Safe only for non-DN sinks (ACL string
13+
%% comparisons, eldap:equalityMatch/2 filter values). To build a DN, use
14+
%% rabbit_ldap_rfc4514:fill_dn/2, which RFC 4514-escapes substituted values.
1215
fill(Fmt, []) ->
1316
binary_to_list(iolist_to_binary(Fmt));
1417

deps/rabbitmq_auth_backend_ldap/src/rabbit_ldap_rfc4514.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
%% RFC 4514 — String Representation of Distinguished Names.
99
%%
1010
%% This module escapes values for safe inclusion in DN attribute value
11-
%% positions, preventing DN injection when untrusted input (usernames,
12-
%% vhost names, resource names) is substituted into DN templates.
11+
%% positions when input such as usernames, vhost names, or resource names is
12+
%% substituted into DN templates.
1313
-module(rabbit_ldap_rfc4514).
1414

1515
-export([escape_value/1, fill_dn/2]).

deps/rabbitmq_auth_backend_ldap/test/unit_SUITE.erl

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ all() ->
1919
ad_fill,
2020
rfc4514_escape_value,
2121
rfc4514_fill_dn,
22+
dn_lookup_fallback_dn_escaping,
2223
user_dn_pattern_gh_7161,
2324
format_different_types_of_ldap_attribute_values,
2425
ldap_log_domain_routing,
@@ -84,7 +85,7 @@ rfc4514_fill_dn(_Config) ->
8485
F = fun(Fmt, Args, Res) ->
8586
?assertEqual(Res, rabbit_ldap_rfc4514:fill_dn(Fmt, Args))
8687
end,
87-
%% DN injection prevented
88+
%% A comma in the substituted value is escaped
8889
F("cn=${username},ou=People", [{username, "user,ou=Evil"}],
8990
"cn=user\\,ou=Evil,ou=People"),
9091
%% user_dn is NOT escaped (it is already a complete DN)
@@ -97,6 +98,36 @@ rfc4514_fill_dn(_Config) ->
9798
"cn=x\\,y,dc=b"),
9899
ok.
99100

101+
dn_lookup_fallback_dn_escaping(_Config) ->
102+
PrevPattern = application:get_env(rabbitmq_auth_backend_ldap, user_dn_pattern),
103+
PrevLog = application:get_env(rabbitmq_auth_backend_ldap, log),
104+
ok = application:set_env(rabbitmq_auth_backend_ldap, log, false),
105+
ok = application:set_env(rabbitmq_auth_backend_ldap, user_dn_pattern,
106+
"cn=${username},ou=People,dc=example,dc=com"),
107+
try
108+
%% No DN-special characters: escaping is a no-op
109+
?assertEqual(rabbit_auth_backend_ldap:fill_user_dn_pattern("alice"),
110+
rabbit_auth_backend_ldap:escaped_user_dn("alice")),
111+
?assertEqual("cn=alice,ou=People,dc=example,dc=com",
112+
rabbit_auth_backend_ldap:escaped_user_dn("alice")),
113+
%% A comma in the substituted value is escaped
114+
?assertEqual("cn=evil\\,ou=admins,ou=People,dc=example,dc=com",
115+
rabbit_auth_backend_ldap:escaped_user_dn("evil,ou=admins")),
116+
%% A binary username (the form used at runtime) is handled identically
117+
?assertEqual("cn=evil\\,ou=admins,ou=People,dc=example,dc=com",
118+
rabbit_auth_backend_ldap:escaped_user_dn(<<"evil,ou=admins">>)),
119+
%% Bare fill leaves the substituted value unescaped
120+
?assertEqual("cn=evil,ou=admins,ou=People,dc=example,dc=com",
121+
rabbit_auth_backend_ldap:fill_user_dn_pattern("evil,ou=admins"))
122+
after
123+
restore_env(user_dn_pattern, PrevPattern),
124+
restore_env(log, PrevLog)
125+
end,
126+
ok.
127+
128+
restore_env(Key, {ok, V}) -> application:set_env(rabbitmq_auth_backend_ldap, Key, V);
129+
restore_env(Key, undefined) -> application:unset_env(rabbitmq_auth_backend_ldap, Key).
130+
100131
ad_fill(_Config) ->
101132
F = fun(Fmt, Args, Res) ->
102133
?assertEqual(Res, rabbit_auth_backend_ldap_util:fill(Fmt, Args))

0 commit comments

Comments
 (0)