org.cristalise.lookup.ldap.LDAPLookupUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cristalise-ldap Show documentation
Show all versions of cristalise-ldap Show documentation
CRISTAL LDAP Lookup, Authentication and Property Storage Module
/**
* This file is part of the CRISTAL-iSE LDAP lookup plugin.
* Copyright (c) 2001-2016 The CRISTAL Consortium. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* http://www.fsf.org/licensing/licenses/lgpl.html
*/
package org.cristalise.lookup.ldap;
import java.security.KeyManagementException;
//import netscape.ldap.*;
//import netscape.ldap.util.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Random;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.cristalise.kernel.common.ObjectAlreadyExistsException;
import org.cristalise.kernel.common.ObjectCannotBeUpdated;
import org.cristalise.kernel.common.ObjectNotFoundException;
import org.cristalise.kernel.utils.Logger;
import com.novell.ldap.LDAPAttribute;
import com.novell.ldap.LDAPAttributeSet;
import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPDN;
import com.novell.ldap.LDAPEntry;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPJSSEStartTLSFactory;
import com.novell.ldap.LDAPModification;
import com.novell.ldap.LDAPSearchConstraints;
import com.novell.ldap.LDAPSearchResults;
import com.novell.ldap.LDAPSocketFactory;
import com.novell.ldap.util.Base64;
final public class LDAPLookupUtils {
static final char[] META_CHARS = { '+', '=', '"', ',', '<', '>', ';', '/' };
static final String[] META_ESCAPED = { "2B", "3D", "22", "2C", "3C", "3E", "3B", "2F" };
private static final Random RANDOM = new SecureRandom();
static public LDAPEntry getEntry(LDAPConnection ld, String dn, int dereference)
throws ObjectNotFoundException {
try {
LDAPSearchConstraints searchCons = new LDAPSearchConstraints();
searchCons.setBatchSize(0);
searchCons.setDereference(dereference);
LDAPEntry thisEntry = ld.read(dn, searchCons);
if (thisEntry != null) return thisEntry;
}
catch (LDAPException ex) {
throw new ObjectNotFoundException("LDAP Exception for dn:" + dn + ": \n" + ex.getMessage());
}
throw new ObjectNotFoundException(dn + " does not exist");
}
public static String generateUserPassword(String pass) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA");
md.reset();
String salt = generateSalt(16);
md.update((pass + salt).getBytes());
byte[] hash = md.digest();
byte[] saltBytes = salt.getBytes();
byte[] allBytes = new byte[hash.length + saltBytes.length];
System.arraycopy(hash, 0, allBytes, 0, hash.length);
System.arraycopy(saltBytes, 0, allBytes, hash.length, saltBytes.length);
StringBuffer encPassword = new StringBuffer("{SSHA}");
encPassword.append(Base64.encode(allBytes));
return encPassword.toString();
}
public static String generateSalt(int size) {
byte[] salt = new byte[size];
RANDOM.nextBytes(salt);
return String.valueOf(salt);
}
/**
* Utility method to connect to an LDAP server
*
* @param lp
* LDAP properties to connect with
* @return a novell LDAPConnection object
* @throws LDAPException when the connection was unsuccessful
*/
public static LDAPConnection createConnection(LDAPProperties lp) throws LDAPException {
LDAPConnection ld;
if (lp.mUseTLS) {
try {
LDAPSocketFactory lsf;
if (lp.mIgnoreCertErrors) lsf = new LDAPJSSEStartTLSFactory(getPermissiveSocketFactory());
else lsf = new LDAPJSSEStartTLSFactory();
LDAPConnection.setSocketFactory(lsf);
}
catch (Exception ex) {
Logger.error(ex);
Logger.die("Could not enable TLS over LDAP");
}
}
if (lp.mTimeOut == 0) ld = new LDAPConnection();
else ld = new LDAPConnection(lp.mTimeOut);
Logger.msg(3, "LDAPLookup - connecting to " + lp.mHost);
ld.connect(lp.mHost, Integer.valueOf(lp.mPort).intValue());
if (lp.mUseTLS) {
try {
ld.startTLS();
}
catch (Exception ex) {
Logger.error(ex);
Logger.die("Could not enable TLS over LDAP");
}
}
Logger.msg(3, "LDAPLookup - authenticating user:" + lp.mUser);
ld.bind(LDAPConnection.LDAP_V3, lp.mUser, String.valueOf(lp.mPassword).getBytes());
Logger.msg(3, "LDAPLookup - authentication successful");
LDAPSearchConstraints searchCons = new LDAPSearchConstraints();
searchCons.setMaxResults(0);
ld.setConstraints(searchCons);
return ld;
}
static public SSLSocketFactory getPermissiveSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext.getSocketFactory();
}
// Given a DN, return an LDAP Entry
static public LDAPEntry getEntry(LDAPConnection ld, String dn) throws ObjectNotFoundException {
return getEntry(ld, dn, LDAPSearchConstraints.DEREF_NEVER);
}
static public String getFirstAttributeValue(LDAPEntry anEntry, String attribute) throws ObjectNotFoundException {
LDAPAttribute attr = anEntry.getAttribute(attribute);
if (attr == null) throw new ObjectNotFoundException("No attributes named '" + attribute + "'");
return (String) attr.getStringValues().nextElement();
}
static public String[] getAllAttributeValues(LDAPEntry anEntry, String attribute) {
LDAPAttribute attr = anEntry.getAttribute(attribute);
if (attr != null) return attr.getStringValueArray();
return new String[0];
}
static public boolean existsAttributeValue(LDAPEntry anEntry, String attribute, String value) {
LDAPAttribute attr = anEntry.getAttribute(attribute);
if (attr != null) {
String[] attrValues = new String[attr.size()];
attrValues = attr.getStringValueArray();
for (int i = 0; i < attr.size(); i++)
if (attrValues[i].equalsIgnoreCase(value)) return true;
}
return false;
}
static public boolean hasOneAttributeValue(LDAPEntry anEntry, String attribute) throws ObjectNotFoundException {
int j = 0;
LDAPAttribute attr = anEntry.getAttribute(attribute);
if (attr == null) throw new ObjectNotFoundException("No attributes named '" + attribute + "'");
j = attr.size();
return j == 1;
}
// this is for a single-valued attribute
static public void setAttributeValue(LDAPConnection ld, LDAPEntry anEntry, String attribute, String newValue)
throws ObjectNotFoundException, ObjectCannotBeUpdated
{
try {
if (!hasOneAttributeValue(anEntry, attribute))
throw new ObjectCannotBeUpdated("Attribute " + attribute + " of entry " + anEntry.getDN() + " has more than one value");
}
catch (ObjectNotFoundException ex) {
addAttributeValue(ld, anEntry, attribute, newValue);
}
try {
ld.modify(anEntry.getDN(), new LDAPModification(LDAPModification.REPLACE, new LDAPAttribute(attribute, newValue)));
}
catch (LDAPException ex) {
Logger.error(ex);
throw new ObjectCannotBeUpdated("Attribute " + attribute + " of entry " + anEntry.getDN() + " could not be modified");
}
}
// this is for a multi-valued attribute eg uniqueMember
static public void addAttributeValue(LDAPConnection ld, LDAPEntry anEntry, String attribute, String value) throws ObjectCannotBeUpdated {
try {
ld.modify(anEntry.getDN(), new LDAPModification(LDAPModification.ADD, new LDAPAttribute(attribute, value)));
}
catch (LDAPException ex) {
Logger.error(ex);
throw new ObjectCannotBeUpdated("Attribute " + attribute + " of entry " + anEntry.getDN() + " could not be added.");
}
}
// this is for a multi-valued attribute eg uniqueMember
static public void removeAttributeValue(LDAPConnection ld, LDAPEntry anEntry, String attribute, String value) throws ObjectCannotBeUpdated {
try {
ld.modify(anEntry.getDN(), new LDAPModification(LDAPModification.DELETE, new LDAPAttribute(attribute, value)));
}
catch (LDAPException ex) {
Logger.error(ex);
throw new ObjectCannotBeUpdated("Attribute " + attribute + " of entry " + anEntry.getDN() + " could not be deleted");
}
}
static public boolean exists(LDAPConnection ld, String name) {
try {
String[] attr = { LDAPConnection.NO_ATTRS };
LDAPEntry anEntry = ld.read(name, attr);
if (anEntry != null)
return true;
}
catch (LDAPException ex) {
Logger.debug(9, "LDAPLookupUtils.exists(" + name + ": " + ex.getMessage());
return false;
}
return false;
}
static public void addEntry(LDAPConnection ld, LDAPEntry myEntry) throws ObjectAlreadyExistsException, LDAPException {
try {
ld.add(myEntry);
}
catch (LDAPException ex) {
if (ex.getResultCode() == LDAPException.ENTRY_ALREADY_EXISTS)
throw new ObjectAlreadyExistsException("Entry already present." + myEntry.getDN());
throw ex;
}
}
static public boolean hasChildren(LDAPConnection ld, String dn, String filter) {
String[] attr = { LDAPConnection.NO_ATTRS };
LDAPSearchConstraints searchCons = new LDAPSearchConstraints();
searchCons.setBatchSize(0);
searchCons.setDereference(LDAPSearchConstraints.DEREF_NEVER);
try {
LDAPSearchResults res = ld.search(dn, LDAPConnection.SCOPE_ONE, filter, attr, false, searchCons);
if (res.hasMore()) return true;
}
catch (LDAPException ex) {
Logger.error(ex);
}
return false;
}
// returns list of dns
static public String[] getChildrenDNs(LDAPConnection ld, String dn, String filter) {
String[] result = null;
String[] attr = { LDAPConnection.NO_ATTRS };
LDAPSearchConstraints searchCons = new LDAPSearchConstraints();
searchCons.setBatchSize(0);
searchCons.setDereference(LDAPSearchConstraints.DEREF_NEVER);
try {
LDAPSearchResults res = ld.search(dn, LDAPConnection.SCOPE_ONE, filter, attr, false, searchCons);
result = new String[res.getCount()];
int i = 0;
while (res.hasMore()) {
LDAPEntry findEntry = res.next();
if (findEntry != null) result[i++] = new String(findEntry.getDN());
}
}
catch (Exception ex) {
Logger.error(ex);
}
return result;
}
static public void delete(LDAPConnection ld, String dn) throws LDAPException {
try {
Logger.msg(7, "LDAPLookupUtils.delete() - " + dn);
ld.delete(dn);
}
catch (LDAPException ex) {
Logger.error("LDAPLookupUtils.remove() - Cannot remove " + dn + ": " + ex.getMessage());
throw ex;
}
}
// param dn is the DN of the name
// param name is the name of the node (also the RDN)
// example: cn=lab27,o=cern.ch lab27
// example: cn=product, cn=domain, cn=lab27, cn= cristal2, o=cern.ch product
static public void createCristalContext(LDAPConnection ld, String dn) {
if (LDAPLookupUtils.exists(ld, dn)) return;
try {
String name = LDAPDN.explodeDN(dn, true)[0];
LDAPAttributeSet attrs = new LDAPAttributeSet();
attrs.add(new LDAPAttribute("cn", name));
String objectclass_values[] = new String[1];
objectclass_values[0] = "cristalcontext";
if (name.equals("last")) attrs.add(new LDAPAttribute("intsyskey", "0"));
attrs.add(new LDAPAttribute("objectclass", objectclass_values));
LDAPLookupUtils.addEntry(ld, new LDAPEntry(dn, attrs));
}
catch (Exception ex) {
Logger.error(ex);
Logger.die("Error creating CRISTAL LDAP roots. Is the cristal.schema configured correctly in the LDAP server?");
}
}
static public void createOrganizationContext(LDAPConnection ld, String dn) {
if (LDAPLookupUtils.exists(ld, dn)) return;
try {
String name = LDAPDN.explodeDN(dn, true)[0];
LDAPAttributeSet attrs = new LDAPAttributeSet();
// No idea why this worked, or why it suddenly stopped working when we moved to maven
// attrs.add(new LDAPAttribute("objectclass","top"));
attrs.add(new LDAPAttribute("objectclass", "organization"));
attrs.add(new LDAPAttribute("o", name));
LDAPLookupUtils.addEntry(ld, new LDAPEntry(dn, attrs));
}
catch (Exception ex) {
Logger.msg(ex.toString());
}
}
public static String escapeDN(String name) {
// From RFC 2253 and the / character for JNDI
if (name == null) return null;
String escapedStr = new String(name);
// Backslash is both a Java and an LDAP escape character, so escape it first
escapedStr = escapedStr.replaceAll("\\\\", "\\\\");
// Positional characters - see RFC 2253
escapedStr = escapedStr.replaceAll("^#", "\\\\23"); // TODO: active directory requires hash to be escaped everywhere
escapedStr = escapedStr.replaceAll("^ | $", "\\\\20");
for (int i = 0; i < META_CHARS.length; i++) {
escapedStr = escapedStr.replaceAll("\\" + META_CHARS[i], "\\\\" + META_ESCAPED[i]);
}
if (!name.equals(escapedStr)) Logger.msg(3, "LDAP DN " + name + " escaped to " + escapedStr);
return escapedStr;
}
public static String unescapeDN(String dn) {
// From RFC 2253 and the / character for JNDI
String unescapedStr = new String(dn);
// Positional characters - see RFC 2253
unescapedStr = unescapedStr.replaceAll("^\\\\23", "#"); // TODO: active directory requires hash to be escaped everywhere
unescapedStr = unescapedStr.replaceAll("^\\\\20|\\\\20$", " ");
for (int i = 0; i < META_CHARS.length; i++) {
unescapedStr = unescapedStr.replaceAll("\\\\" + META_ESCAPED[i], "" + META_CHARS[i]);
}
// Any remaining escaped backslashes
unescapedStr = unescapedStr.replaceAll("\\\\", "\\");
if (!dn.equals(unescapedStr)) Logger.msg(3, "LDAP DN " + dn + " unescaped to " + unescapedStr);
return unescapedStr;
}
public static String escapeSearchFilter(String filter) {
// From RFC 2254
String escapedStr = new String(filter);
escapedStr = escapedStr.replaceAll("\\\\", "\\\\5c");
// escapedStr = escapedStr.replaceAll("\\*","\\\\2a"); // we need stars for searching
escapedStr = escapedStr.replaceAll("\\(", "\\\\28");
escapedStr = escapedStr.replaceAll("\\)", "\\\\29");
if (!filter.equals(escapedStr)) Logger.msg(3, "LDAP Search Filter " + filter + " escaped to " + escapedStr);
return escapedStr;
}
}