blob: 15b1b56956f97f8db732c056893074b5bf0ab598 [file] [log] [blame]
/*
* user authentication via LDAP
*/
package de.ids_mannheim.korap.authentication;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.text.StringSubstitutor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.unboundid.ldap.sdk.BindResult;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPSearchException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.util.NotNull;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
import com.unboundid.util.ssl.TrustStoreTrustManager;
import de.ids_mannheim.korap.server.EmbeddedLdapServer;
/**
* LDAP Login
*
* @author bodmer, margaretha, kupietz
*/
public class LdapAuth3 {
public static final int LDAP_AUTH_ROK = 0;
public static final int LDAP_AUTH_RCONNECT = 1; // cannot connect to LDAP Server
public static final int LDAP_AUTH_RINTERR = 2; // internal error: cannot verify User+Pwd.
public static final int LDAP_AUTH_RUNKNOWN = 3; // User Account or Pwd unknown;
public static final int LDAP_AUTH_RLOCKED = 4; // User Account locked;
public static final int LDAP_AUTH_RNOTREG = 5; // User known, but has not registered to KorAP/C2 Service yet;
public static final int LDAP_AUTH_RNOEMAIL = 6; // cannot obtain email for sUserDN
public static final int LDAP_AUTH_RNAUTH = 7; // User Account or Pwd unknown, or not authorized
final static Boolean DEBUGLOG = false; // log debug output.
private static Logger jlog = LogManager.getLogger(LdapAuth3.class);
public static String getErrMessage(int code) {
switch (code) {
case LDAP_AUTH_ROK:
return "LDAP Authentication successful.";
case LDAP_AUTH_RCONNECT:
return "LDAP Authentication: connecting to LDAP Server failed!";
case LDAP_AUTH_RINTERR:
return "LDAP Authentication failed due to an internal error!";
case LDAP_AUTH_RUNKNOWN:
return "LDAP Authentication failed due to unknown user or password!";
case LDAP_AUTH_RLOCKED:
return "LDAP Authentication: known user is locked!";
case LDAP_AUTH_RNOTREG:
return "LDAP Authentication: known user, but not registered for this service!";
case LDAP_AUTH_RNOEMAIL:
return "LDAP Authentication: known user, but cannot obtain email!";
case LDAP_AUTH_RNAUTH:
return "LDAP Authentication: unknown user or password, or user is locked or not authorized!";
default:
return "LDAP Authentication failed with unknown error code!";
}
}
public static int login(String login, String password, String ldapConfigFilename) throws LDAPException {
LDAPConfig ldapConfig = new LDAPConfig(ldapConfigFilename);
login = Filter.encodeValue(login);
password = Filter.encodeValue(password);
if (ldapConfig.useEmbeddedServer) {
try {
EmbeddedLdapServer.startIfNotRunning(ldapConfig);
} catch (GeneralSecurityException | UnknownHostException | LDAPException e) {
throw new RuntimeException(e);
}
}
LdapAuth3Result ldapAuth3Result = search(login, password, ldapConfig, !ldapConfig.searchFilter.contains("${password}"), true);
SearchResult srchRes = ldapAuth3Result.getSearchResultValue();
if (ldapAuth3Result.getErrorCode() != 0 || srchRes == null || srchRes.getEntryCount() == 0) {
if (DEBUGLOG) System.out.printf("Finding '%s': no entry found!\n", login);
return ldapAuth3Result.getErrorCode();
}
return LDAP_AUTH_ROK;
}
@NotNull
public static LdapAuth3Result search(String login, String password, LDAPConfig ldapConfig, boolean bindWithFoundDN, boolean applyExtraFilters) {
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("login", login);
valuesMap.put("password", password);
StringSubstitutor sub = new StringSubstitutor(valuesMap);
String searchFilterInstance = sub.replace(ldapConfig.searchFilter);
valuesMap.clear();
valuesMap.put("login", login);
sub = new StringSubstitutor(valuesMap);
String insensitiveSearchFilter = sub.replace(ldapConfig.searchFilter);
if (DEBUGLOG) {
//System.out.printf("LDAP Version = %d.\n", LDAPConnection.LDAP_V3);
System.out.printf("LDAP Host & Port = '%s':%d.\n", ldapConfig.host, ldapConfig.port);
System.out.printf("Login User = '%s'\n", login);
System.out.println("LDAPS " + ldapConfig.useSSL);
}
LDAPConnection lc;
if (ldapConfig.useSSL) {
try {
SSLUtil sslUtil;
if (ldapConfig.trustStorePath != null && !ldapConfig.trustStorePath.isEmpty()) {
sslUtil = new SSLUtil(new TrustStoreTrustManager(ldapConfig.trustStorePath));
} else {
sslUtil = new SSLUtil(new TrustAllTrustManager());
}
if (ldapConfig.additionalCipherSuites != null && !ldapConfig.additionalCipherSuites.isEmpty()) {
addSSLCipherSuites(ldapConfig.additionalCipherSuites);
}
SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory();
lc = new LDAPConnection(socketFactory);
} catch (GeneralSecurityException e) {
System.err.printf("Error: login: Connecting to LDAPS Server: failed: '%s'!\n", e);
ldapTerminate(null);
return new LdapAuth3Result(null, LDAP_AUTH_RCONNECT);
}
} else {
lc = new LDAPConnection();
}
try {
lc.connect(ldapConfig.host, ldapConfig.port);
if (DEBUGLOG && ldapConfig.useSSL) System.out.println("LDAPS Connection = OK\n");
if (DEBUGLOG && !ldapConfig.useSSL) System.out.println("LDAP Connection = OK\n");
} catch (LDAPException e) {
String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e);
System.err.printf("Error: login: Connecting to LDAP Server: failed: '%s'!\n", fullStackTrace);
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RCONNECT);
}
if (DEBUGLOG) System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0);
try {
// bind to server:
if (DEBUGLOG) System.out.printf("Binding with '%s' ...\n", ldapConfig.sLoginDN);
lc.bind(ldapConfig.sLoginDN, ldapConfig.sPwd);
if (DEBUGLOG) System.out.print("Binding: OK.\n");
} catch (LDAPException e) {
System.err.printf("Error: login: Binding failed: '%s'!\n", e);
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RINTERR);
}
if (DEBUGLOG) System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0);
if (DEBUGLOG) System.out.printf("Finding user '%s'...\n", login);
SearchResult srchRes = null;
try {
if (DEBUGLOG) System.out.printf("Searching with searchFilter: '%s'.\n", insensitiveSearchFilter);
srchRes = lc.search(ldapConfig.searchBase, SearchScope.SUB, searchFilterInstance);
if (DEBUGLOG) System.out.printf("Found '%s': %d entries.\n", login, srchRes.getEntryCount());
} catch (LDAPSearchException e) {
System.err.printf("Error: Search for User failed: '%s'!\n", e);
}
if (srchRes == null || srchRes.getEntryCount() == 0) {
if (DEBUGLOG) System.out.printf("Finding '%s': no entry found!\n", login);
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RUNKNOWN);
}
if (bindWithFoundDN) {
String matchedDN = srchRes.getSearchEntries().get(0).getDN();
if (DEBUGLOG) System.out.printf("Requested bind for found user %s' failed.\n", matchedDN);
try {
// bind to server:
if (DEBUGLOG) System.out.printf("Binding with '%s' ...\n", matchedDN);
BindResult bindResult = lc.bind(matchedDN, password);
if (DEBUGLOG) System.out.print("Binding: OK.\n");
if (!bindResult.getResultCode().equals(ResultCode.SUCCESS)) {
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RUNKNOWN);
}
} catch (LDAPException e) {
System.err.printf("Error: login: Binding failed: '%s'!\n", e);
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RUNKNOWN);
}
}
if (applyExtraFilters) {
if (ldapConfig.authFilter != null && !ldapConfig.authFilter.isEmpty()) {
srchRes = applyAdditionalFilter(login, ldapConfig, ldapConfig.authFilter, searchFilterInstance, lc);
if (srchRes == null || srchRes.getEntryCount() == 0) {
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RNOTREG);
}
}
if (ldapConfig.userNotBlockedFilter != null && !ldapConfig.userNotBlockedFilter.isEmpty()) {
srchRes = applyAdditionalFilter(login, ldapConfig, ldapConfig.userNotBlockedFilter, searchFilterInstance, lc);
if (srchRes == null || srchRes.getEntryCount() == 0) {
ldapTerminate(lc);
return new LdapAuth3Result(null, LDAP_AUTH_RLOCKED);
}
}
}
ldapTerminate(lc);
return new LdapAuth3Result(srchRes, LDAP_AUTH_ROK);
}
private static SearchResult applyAdditionalFilter(String login, LDAPConfig ldapConfig, String searchFilterInstance, String extraFilter, LDAPConnection lc) {
SearchResult srchRes;
srchRes = null;
try {
String combindedFilterInstance = "(&" + searchFilterInstance + extraFilter + ")";
if (DEBUGLOG) System.out.printf("Searching with additional Filter: '%s'.\n", extraFilter);
srchRes = lc.search(ldapConfig.searchBase, SearchScope.SUB, combindedFilterInstance);
if (DEBUGLOG) System.out.printf("Found '%s': %d entries.\n", login, srchRes.getEntryCount());
} catch (LDAPSearchException e) {
System.err.printf("Error: Search for User failed: '%s'!\n", e);
}
return srchRes;
}
public static String getEmail(String sUserDN, String ldapConfigFilename) throws LDAPException {
String sUserPwd = "*";
LDAPConfig ldapConfig = new LDAPConfig(ldapConfigFilename);
final String emailAttribute = ldapConfig.emailAttribute;
SearchResult searchResult = search(sUserDN, sUserPwd, ldapConfig, false, false).getSearchResultValue();
if (searchResult == null) {
return null;
}
for (SearchResultEntry entry : searchResult.getSearchEntries()) {
String mail = entry.getAttributeValue(emailAttribute);
if (mail != null) {
return mail;
}
}
return null;
}
public static String getUsername(String sUserDN, String ldapConfigFilename) throws LDAPException {
String sUserPwd = "*";
LDAPConfig ldapConfig = new LDAPConfig(ldapConfigFilename);
final String idsC2Attribute = "idsC2Profile";
final String uidAttribute = "uid";
SearchResult searchResult = search(sUserDN, sUserPwd, ldapConfig, false, false)
.getSearchResultValue();
if (searchResult == null) {
return null;
}
String username = null;
for (SearchResultEntry entry : searchResult.getSearchEntries()) {
username = entry.getAttributeValue(idsC2Attribute);
if (username == null) {
username = entry.getAttributeValue(uidAttribute);
jlog.warn("idsC2Profile not found for uid: "+username);
}
}
return username;
}
public static void ldapTerminate(LDAPConnection lc) {
if (DEBUGLOG) System.out.println("Terminating...");
if (lc != null) {
lc.close(null);
}
if (DEBUGLOG) System.out.println("closing connection: done.\n");
}
private static void addSSLCipherSuites(String ciphersCsv) {
// add e.g. TLS_RSA_WITH_AES_256_GCM_SHA384
Set<String> ciphers = new HashSet<>();
ciphers.addAll(SSLUtil.getEnabledSSLCipherSuites());
ciphers.addAll(Arrays.asList(ciphersCsv.split(", *")));
SSLUtil.setEnabledSSLCipherSuites(ciphers);
}
public static class LdapAuth3Result {
final int errorCode;
final Object value;
public LdapAuth3Result(Object value, int errorCode) {
this.errorCode = errorCode;
this.value = value;
}
public int getErrorCode() {
return errorCode;
}
public Object getValue() {
return value;
}
public SearchResult getSearchResultValue() {
return (SearchResult) value;
}
}
}