Skip to content

Commit

Permalink
Allows for configuration of LDAP referral following (#2135) (#2661)
Browse files Browse the repository at this point in the history
* Correct Java Native Access Description in THIRD-PARTY.txt

Signed-off-by: Stephen Crawford <steecraw@amazon.com>
(cherry picked from commit 4846396)

Co-authored-by: Stephen Crawford <65832608+scrawfor99@users.noreply.github.com>
  • Loading branch information
1 parent 2d9cebf commit 2f58b20
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ public class LDAPAuthenticationBackend implements AuthenticationBackend {
private final WildcardMatcher allowlistedCustomLdapAttrMatcher;

private final String[] returnAttributes;
private final boolean shouldFollowReferrals;

public LDAPAuthenticationBackend(final Settings settings, final Path configPath) {
this.settings = settings;
this.configPath = configPath;
this.userBaseSettings = getUserBaseSettings(settings);
this.returnAttributes = settings.getAsList(ConfigConstants.LDAP_RETURN_ATTRIBUTES, Arrays.asList(ReturnAttributes.ALL.value())).toArray(new String[0]);
this.shouldFollowReferrals = settings.getAsBoolean(ConfigConstants.FOLLOW_REFERRALS, ConfigConstants.FOLLOW_REFERRALS_DEFAULT);

customAttrMaxValueLen = settings.getAsInt(ConfigConstants.LDAP_CUSTOM_ATTR_MAXVAL_LEN, 36);
checkForDeprecatedSetting(settings, ConfigConstants.LDAP_CUSTOM_ATTR_WHITELIST, ConfigConstants.LDAP_CUSTOM_ATTR_ALLOWLIST);
Expand All @@ -90,7 +92,7 @@ public User authenticate(final AuthCredentials credentials) throws OpenSearchSec
try {
ldapConnection = LDAPAuthorizationBackend.getConnection(settings, configPath);

entry = exists(user, ldapConnection, settings, userBaseSettings, this.returnAttributes);
entry = exists(user, ldapConnection, settings, userBaseSettings, this.returnAttributes, this.shouldFollowReferrals);

// fake a user that no exists
// makes guessing if a user exists or not harder when looking on the
Expand Down Expand Up @@ -164,7 +166,7 @@ public boolean exists(final User user) {

try {
ldapConnection = LDAPAuthorizationBackend.getConnection(settings, configPath);
LdapEntry userEntry = exists(userName, ldapConnection, settings, userBaseSettings, this.returnAttributes);
LdapEntry userEntry = exists(userName, ldapConnection, settings, userBaseSettings, this.returnAttributes, this.shouldFollowReferrals);
boolean exists = userEntry != null;

if(exists) {
Expand Down Expand Up @@ -205,19 +207,19 @@ static List<Map.Entry<String, Settings>> getUserBaseSettings(Settings settings)
}

static LdapEntry exists(final String user, Connection ldapConnection, Settings settings,
List<Map.Entry<String, Settings>> userBaseSettings, String[] returnAttributes) throws Exception {
List<Map.Entry<String, Settings>> userBaseSettings, String[] returnAttributes, final boolean shouldFollowReferrals) throws Exception {
if (settings.getAsBoolean(ConfigConstants.LDAP_FAKE_LOGIN_ENABLED, false)
|| settings.getAsBoolean(ConfigConstants.LDAP_SEARCH_ALL_BASES, false)
|| settings.hasValue(ConfigConstants.LDAP_AUTHC_USERBASE)) {
return existsSearchingAllBases(user, ldapConnection, userBaseSettings, returnAttributes);
return existsSearchingAllBases(user, ldapConnection, userBaseSettings, returnAttributes, shouldFollowReferrals);
} else {
return existsSearchingUntilFirstHit(user, ldapConnection, userBaseSettings, returnAttributes);
return existsSearchingUntilFirstHit(user, ldapConnection, userBaseSettings, returnAttributes, shouldFollowReferrals);
}

}

private static LdapEntry existsSearchingUntilFirstHit(final String user, Connection ldapConnection,
List<Map.Entry<String, Settings>> userBaseSettings, final String[] returnAttributes) throws Exception {
List<Map.Entry<String, Settings>> userBaseSettings, final String[] returnAttributes, final boolean shouldFollowReferrals) throws Exception {
final String username = user;

final boolean isDebugEnabled = log.isDebugEnabled();
Expand All @@ -232,7 +234,7 @@ private static LdapEntry existsSearchingUntilFirstHit(final String user, Connect
baseSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_USERBASE),
f,
SearchScope.SUBTREE,
returnAttributes);
returnAttributes, shouldFollowReferrals);

if (isDebugEnabled) {
log.debug("Results for LDAP search for {} in base {} is {}", user, entry.getKey(), result);
Expand All @@ -247,7 +249,7 @@ private static LdapEntry existsSearchingUntilFirstHit(final String user, Connect
}

private static LdapEntry existsSearchingAllBases(final String user, Connection ldapConnection,
List<Map.Entry<String, Settings>> userBaseSettings, final String[] returnAttributes) throws Exception {
List<Map.Entry<String, Settings>> userBaseSettings, final String[] returnAttributes, final boolean shouldFollowReferrals) throws Exception {
final String username = user;
Set<LdapEntry> result = new HashSet<>();

Expand All @@ -263,7 +265,7 @@ private static LdapEntry existsSearchingAllBases(final String user, Connection l
baseSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_USERBASE),
f,
SearchScope.SUBTREE,
returnAttributes);
returnAttributes, shouldFollowReferrals);

if (isDebugEnabled) {
log.debug("Results for LDAP search for " + user + " in base " + entry.getKey() + ":\n" + result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ public class LDAPAuthorizationBackend implements AuthorizationBackend {
private final Settings settings;
private final WildcardMatcher skipUsersMatcher;
private final WildcardMatcher nestedRoleMatcher;

private final Path configPath;
private final List<Map.Entry<String, Settings>> roleBaseSettings;
private final List<Map.Entry<String, Settings>> userBaseSettings;

private final String[] returnAttributes;
private final boolean shouldFollowReferrals;

public LDAPAuthorizationBackend(final Settings settings, final Path configPath) {
this.settings = settings;
Expand All @@ -115,6 +115,8 @@ public LDAPAuthorizationBackend(final Settings settings, final Path configPath)
this.roleBaseSettings = getRoleSearchSettings(settings);
this.userBaseSettings = LDAPAuthenticationBackend.getUserBaseSettings(settings);
this.returnAttributes = settings.getAsList(ConfigConstants.LDAP_RETURN_ATTRIBUTES, Arrays.asList(ReturnAttributes.ALL.value())).toArray(new String[0]);
this.shouldFollowReferrals = settings.getAsBoolean(ConfigConstants.FOLLOW_REFERRALS, ConfigConstants.FOLLOW_REFERRALS_DEFAULT);

}

@SuppressWarnings("removal")
Expand Down Expand Up @@ -728,7 +730,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds)
log.debug("DBGTRACE (4): authenticatedUser="+authenticatedUser+" -> "+Arrays.toString(authenticatedUser.getBytes(StandardCharsets.UTF_8)));
}

entry = LdapHelper.lookup(connection, authenticatedUser, this.returnAttributes);
entry = LdapHelper.lookup(connection, authenticatedUser, this.returnAttributes, this.shouldFollowReferrals);

if (entry == null) {
throw new OpenSearchSecurityException("No user '" + authenticatedUser + "' found");
Expand All @@ -739,7 +741,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds)
if (isDebugEnabled)
log.debug("DBGTRACE (5): authenticatedUser="+user.getName()+" -> "+Arrays.toString(user.getName().getBytes(StandardCharsets.UTF_8)));

entry = LDAPAuthenticationBackend.exists(user.getName(), connection, settings, userBaseSettings, this.returnAttributes);
entry = LDAPAuthenticationBackend.exists(user.getName(), connection, settings, userBaseSettings, this.returnAttributes, this.shouldFollowReferrals);

if (isTraceEnabled) {
log.trace("{} is not a valid DN and was resolved to {}", authenticatedUser, entry);
Expand Down Expand Up @@ -852,7 +854,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds)
List<LdapEntry> rolesResult = LdapHelper.search(connection,
roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_ROLEBASE),
f,
SearchScope.SUBTREE, this.returnAttributes);
SearchScope.SUBTREE, this.returnAttributes, this.shouldFollowReferrals);

if (isTraceEnabled) {
log.trace("Results for LDAP group search for {} in base {}:\n{}", escapedDn, roleSearchSettingsEntry.getKey(), rolesResult);
Expand Down Expand Up @@ -970,7 +972,7 @@ protected Set<LdapName> resolveNestedRoles(final LdapName roleDn, final Connecti
final Set<LdapName> result = new HashSet<>(20);
final HashMultimap<LdapName, Map.Entry<String, Settings>> resultRoleSearchBaseKeys = HashMultimap.create();

final LdapEntry e0 = LdapHelper.lookup(ldapConnection, roleDn.toString(), this.returnAttributes);
final LdapEntry e0 = LdapHelper.lookup(ldapConnection, roleDn.toString(), this.returnAttributes, this.shouldFollowReferrals);

if (e0.getAttribute(userRoleName) != null) {
final Collection<String> userRoles = e0.getAttribute(userRoleName).getStringValues();
Expand Down Expand Up @@ -1023,7 +1025,7 @@ protected Set<LdapName> resolveNestedRoles(final LdapName roleDn, final Connecti
roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_ROLEBASE),
f,
SearchScope.SUBTREE,
this.returnAttributes);
this.returnAttributes, this.shouldFollowReferrals);

if (isTraceEnabled) {
log.trace("Results for LDAP group search for {} in base {}:\n{}", escapedDn, roleSearchBaseSettingsEntry.getKey(), foundEntries);
Expand Down Expand Up @@ -1101,7 +1103,7 @@ private String getRoleFromEntry(final Connection ldapConnection, final LdapName
}

try {
final LdapEntry roleEntry = LdapHelper.lookup(ldapConnection, ldapName.toString(), this.returnAttributes);
final LdapEntry roleEntry = LdapHelper.lookup(ldapConnection, ldapName.toString(), this.returnAttributes, this.shouldFollowReferrals);

if(roleEntry != null) {
final LdapAttribute roleAttribute = roleEntry.getAttribute(role);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public final class ConfigConstants {
public static final String LDAP_AUTHZ_MAX_NESTED_DEPTH = "max_nested_depth";
public static final int LDAP_AUTHZ_MAX_NESTED_DEPTH_DEFAULT = 30;

public static final String FOLLOW_REFERRALS = "follow_referrals";
public static final boolean FOLLOW_REFERRALS_DEFAULT = true;

public static final String LDAP_HOSTS = "hosts";
public static final String LDAP_BIND_DN = "bind_dn";
public static final String LDAP_PASSWORD = "password";
Expand Down
17 changes: 11 additions & 6 deletions src/main/java/com/amazon/dlic/auth/ldap/util/LdapHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,35 @@ public class LdapHelper {
private static SearchFilter ALL = new SearchFilter("(objectClass=*)");
@SuppressWarnings("removal")
public static List<LdapEntry> search(final Connection conn, final String unescapedDn, SearchFilter filter,
final SearchScope searchScope, final String[] returnAttributes) throws LdapException {
final SearchScope searchScope, final String[] returnAttributes, boolean shouldFollowReferrals) throws LdapException {

final SecurityManager sm = System.getSecurityManager();

if (sm != null) {
sm.checkPermission(new SpecialPermission());
}

try {
final String baseDn = escapeDn(unescapedDn);
return AccessController.doPrivileged(new PrivilegedExceptionAction<List<LdapEntry>>() {
@Override
public List<LdapEntry> run() throws Exception {
final List<LdapEntry> entries = new ArrayList<>();
final SearchRequest request = new SearchRequest(baseDn, filter);
request.setReferralHandler(new SearchReferralHandler());

request.setSearchScope(searchScope);
request.setDerefAliases(DerefAliases.ALWAYS);
request.setReturnAttributes(returnAttributes);
final SearchOperation search = new SearchOperation(conn);
// referrals will be followed to build the response

if (shouldFollowReferrals) {
// referrals will be followed to build the response
request.setReferralHandler(new SearchReferralHandler());
}

final Response<SearchResult> r = search.execute(request);
final org.ldaptive.SearchResult result = r.getResult();
entries.addAll(result.getEntries());

return entries;
}
});
Expand All @@ -80,9 +85,9 @@ public List<LdapEntry> run() throws Exception {
}
}

public static LdapEntry lookup(final Connection conn, final String unescapedDn, final String[] returnAttributes) throws LdapException {
public static LdapEntry lookup(final Connection conn, final String unescapedDn, final String[] returnAttributes, boolean shouldFollowReferrals) throws LdapException {

final List<LdapEntry> entries = search(conn, unescapedDn, ALL, SearchScope.OBJECT, returnAttributes);
final List<LdapEntry> entries = search(conn, unescapedDn, ALL, SearchScope.OBJECT, returnAttributes, shouldFollowReferrals);

if (entries.size() == 1) {
return entries.get(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class LDAPAuthenticationBackend2 implements AuthenticationBackend, Destro
private final int customAttrMaxValueLen;
private final WildcardMatcher whitelistedCustomLdapAttrMatcher;
private final String[] returnAttributes;
private final boolean shouldFollowReferrals;

public LDAPAuthenticationBackend2(final Settings settings, final Path configPath) throws SSLConfigException {
this.settings = settings;
Expand All @@ -78,6 +79,7 @@ public LDAPAuthenticationBackend2(final Settings settings, final Path configPath

this.userSearcher = new LDAPUserSearcher(settings);
this.returnAttributes = settings.getAsList(ConfigConstants.LDAP_RETURN_ATTRIBUTES, Arrays.asList(ReturnAttributes.ALL.value())).toArray(new String[0]);
this.shouldFollowReferrals = settings.getAsBoolean(ConfigConstants.FOLLOW_REFERRALS, ConfigConstants.FOLLOW_REFERRALS_DEFAULT);
customAttrMaxValueLen = settings.getAsInt(ConfigConstants.LDAP_CUSTOM_ATTR_MAXVAL_LEN, 36);
whitelistedCustomLdapAttrMatcher = WildcardMatcher.from(settings.getAsList(ConfigConstants.LDAP_CUSTOM_ATTR_WHITELIST,
Collections.singletonList("*")));
Expand Down Expand Up @@ -122,7 +124,7 @@ private User authenticate0(final AuthCredentials credentials) throws OpenSearchS
ldapConnection = connectionFactory.getConnection();
ldapConnection.open();

LdapEntry entry = userSearcher.exists(ldapConnection, user, this.returnAttributes);
LdapEntry entry = userSearcher.exists(ldapConnection, user, this.returnAttributes, this.shouldFollowReferrals);

// fake a user that no exists
// makes guessing if a user exists or not harder when looking on the
Expand Down Expand Up @@ -214,7 +216,7 @@ private boolean exists0(final User user) {
try {
ldapConnection = this.connectionFactory.getConnection();
ldapConnection.open();
LdapEntry userEntry = this.userSearcher.exists(ldapConnection, userName, this.returnAttributes);
LdapEntry userEntry = this.userSearcher.exists(ldapConnection, userName, this.returnAttributes, this.shouldFollowReferrals);

boolean exists = userEntry != null;

Expand Down
Loading

0 comments on commit 2f58b20

Please sign in to comment.