Naveesh Doolhur created RANGER-5774:
---------------------------------------
Summary: RangerAuthenticationProvider LDAP search+bind mode
(empty ranger.ldap.user.dnpattern) bypasses password validation via anonymous
bind
Key: RANGER-5774
URL: https://issues.apache.org/jira/browse/RANGER-5774
Project: Ranger
Issue Type: Bug
Components: Ranger
Affects Versions: 2.9.0, 2.8.0, 2.7.0, 2.6.0, 2.5.0
Environment: Docker
Reporter: Naveesh Doolhur
When ranger.authentication.method=LDAP is configured for search+bind mode
(using ranger.ldap.base.dn / ranger.ldap.bind.dn / ranger.ldap.bind.password,
with ranger.ldap.user.dnpattern left empty as documented/expected for this
mode), any password is accepted for any existing local Ranger username —
authentication is effectively bypassed.
authenticate() always tries getLdapAuthentication() (the DN-pattern method)
before falling back to getLdapBindAuthentication() (the search+bind method):
if ("LDAP".equalsIgnoreCase(rangerAuthenticationMethod)) {
authentication = getLdapAuthentication(authentication);
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
} else {
authentication = getLdapBindAuthentication(authentication);
...
Inside getLdapAuthentication():
Secondary bug, same investigation: even after working around the above (by
setting ranger.ldap.user.dnpattern to a deliberately non-matching placeholder
DN, forcing correct fallthrough to getLdapBindAuthentication()), a correct
password still failed to authenticate whenever
ranger.ldap.group.searchfilter/ranger.ldap.group.searchbase were left empty.
getLdapBindAuthentication(), unlike getLdapAuthentication(), has no guard
around constructing DefaultLdapAuthoritiesPopulator, so it always performs a
group-membership search using those values; an empty filter throws
InvalidSearchFilterException from the JNDI layer after the real user bind
already succeeded, and that exception is caught and treated as an
authentication failure.
Steps to Reproduce:
1. Set ranger.authentication.method=LDAP.
2. Configure ranger.ldap.base.dn, ranger.ldap.bind.dn,
ranger.ldap.bind.password for search+bind mode (per documentation, leave
ranger.ldap.user.dnpattern empty).
3. Attempt to log in (UI or /service/xusers/users REST endpoint via Basic Auth)
as any existing local Ranger username, with any incorrect password.
Expected: Login rejected.
Actual: Login succeeds.
Suggested fix: getLdapAuthentication() should check rangerLdapUserDNPattern for
blank/empty before attempting a bind, and skip directly to
getLdapBindAuthentication() if so, rather than constructing a pattern from an
empty string. Separately, getLdapBindAuthentication() should guard its
DefaultLdapAuthoritiesPopulator construction the same way
getLdapAuthentication() already does (if
(!StringUtil.isEmpty(rangerLdapGroupSearchBase) &&
!StringUtil.isEmpty(rangerLdapGroupSearchFilter))).
String[] userDnPatterns = rangerLdapUserDNPattern.split(";");
bindAuthenticator.setUserDnPatterns(userDnPatterns);
"".split(";") in Java returns [""] — one element, an empty string — not an
empty array. That empty pattern becomes the LDAP bind DN for every login
attempt regardless of username. Binding with an empty DN is a standard
anonymous bind (RFC 4513), which most LDAP servers (including 389 Directory
Server, tested directly) treat as always-successful, ignoring whatever password
was supplied. getLdapAuthentication() therefore returns an "authenticated"
result for any credentials, and getLdapBindAuthentication() — the method
actually intended for this configuration — never runs.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)