/* * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap; import java.net.*;import java.io.*;import java.util.Vector;import java.util.Hashtable; import javax.naming.*;import javax.naming.directory.*;import javax.naming.ldap.*; import com.sun.jndi.ldap.pool.PooledConnection;import com.sun.jndi.ldap.pool.PoolCallback;import com.sun.jndi.ldap.sasl.LdapSasl;import com.sun.jndi.ldap.sasl.SaslInputStream; /** * LDAP (RFC-1777) and LDAPv3 (RFC-2251) compliant client * * This class represents a connection to an LDAP client. * Callers interact with this class at an LDAP operation level. * That is, the caller invokes a method to do a SEARCH or MODRDN * operation and gets back the result. * The caller uses the constructor to create a connection to the server. * It then needs to use authenticate() to perform an LDAP BIND. * Note that for v3, BIND is optional so authenticate() might not * actually send a BIND. authenticate() can be used later on to issue * a BIND, for example, for a v3 client that wants to change the connection's * credentials. *<p> * Multiple LdapCtx might share the same LdapClient. For example, contexts * derived from the same initial context would share the same LdapClient * until changes to a context's properties necessitates its own LdapClient. * LdapClient methods that access shared data are thread-safe (i.e., caller * does not have to sync). *<p> * Fields: * isLdapv3 - no sync; initialized and updated within sync authenticate(); * always updated when connection is "quiet" and not shared; * read access from outside LdapClient not sync * referenceCount - sync within LdapClient; exception is forceClose() which * is used by Connection thread to close connection upon receiving * an Unsolicited Notification. * access from outside LdapClient must sync; * conn - no sync; Connection takes care of its own sync * unsolicited - sync Vector; multiple operations sync'ed * * @author Vincent Ryan * @author Jagane Sundar * @author Rosanna Lee */ public final class LdapClient implements PooledConnection { // ---------------------- Constants ---------------------------------- private static final int debug = 0; static final boolean caseIgnore = true; // Default list of binary attributes private static final Hashtable defaultBinaryAttrs = new Hashtable(23,0.75f); static { defaultBinaryAttrs.put("userpassword", Boolean.TRUE); //2.5.4.35 defaultBinaryAttrs.put("javaserializeddata", Boolean.TRUE); //1.3.6.1.4.1.42.2.27.4.1.8 defaultBinaryAttrs.put("javaserializedobject", Boolean.TRUE); // 1.3.6.1.4.1.42.2.27.4.1.2 defaultBinaryAttrs.put("jpegphoto", Boolean.TRUE); //0.9.2342.19200300.100.1.60 defaultBinaryAttrs.put("audio", Boolean.TRUE); //0.9.2342.19200300.100.1.55 defaultBinaryAttrs.put("thumbnailphoto", Boolean.TRUE); //1.3.6.1.4.1.1466.101.120.35 defaultBinaryAttrs.put("thumbnaillogo", Boolean.TRUE); //1.3.6.1.4.1.1466.101.120.36 defaultBinaryAttrs.put("usercertificate", Boolean.TRUE); //2.5.4.36 defaultBinaryAttrs.put("cacertificate", Boolean.TRUE); //2.5.4.37 defaultBinaryAttrs.put("certificaterevocationlist", Boolean.TRUE); //2.5.4.39 defaultBinaryAttrs.put("authorityrevocationlist", Boolean.TRUE); //2.5.4.38 defaultBinaryAttrs.put("crosscertificatepair", Boolean.TRUE); //2.5.4.40 defaultBinaryAttrs.put("photo", Boolean.TRUE); //0.9.2342.19200300.100.1.7 defaultBinaryAttrs.put("personalsignature", Boolean.TRUE); //0.9.2342.19200300.100.1.53 defaultBinaryAttrs.put("x500uniqueidentifier", Boolean.TRUE); //2.5.4.45 } private static final String DISCONNECT_OID = "1.3.6.1.4.1.1466.20036"; // ----------------------- instance fields ------------------------ boolean isLdapv3; // Used by LdapCtx int referenceCount = 1; // Used by LdapCtx for check for sharing Connection conn; // Connection to server; has reader thread // used by LdapCtx for StartTLS final private PoolCallback pcb; final private boolean pooled; private boolean authenticateCalled = false; //////////////////////////////////////////////////////////////////////////// // // constructor: Create an authenticated connection to server // //////////////////////////////////////////////////////////////////////////// LdapClient(String host, int port, String socketFactory, int connectTimeout, int readTimeout, OutputStream trace, PoolCallback pcb) throws NamingException { if (debug > 0) System.err.println("LdapClient: constructor called " + host + ":" + port ); conn = new Connection(this, host, port, socketFactory, connectTimeout, readTimeout, trace); this.pcb = pcb; pooled = (pcb != null); } synchronized boolean authenticateCalled() { return authenticateCalled; } synchronized LdapResult authenticate(boolean initial, String name, Object pw, int version, String authMechanism, Control[] ctls, Hashtable env) throws NamingException { authenticateCalled = true; try { ensureOpen(); } catch (IOException e) { NamingException ne = new CommunicationException(); ne.setRootCause(e); throw ne; } switch (version) { case LDAP_VERSION3_VERSION2: case LDAP_VERSION3: isLdapv3 = true; break; case LDAP_VERSION2: isLdapv3 = false; break; default: throw new CommunicationException("Protocol version " + version + " not supported"); } LdapResult res = null; if (authMechanism.equalsIgnoreCase("none") || authMechanism.equalsIgnoreCase("anonymous")) { // Perform LDAP bind if we are reauthenticating, using LDAPv2, // supporting failover to LDAPv2, or controls have been supplied. if (!initial || (version == LDAP_VERSION2) || (version == LDAP_VERSION3_VERSION2) || ((ctls != null) && (ctls.length > 0))) { try { // anonymous bind; update name/pw for LDAPv2 retry res = ldapBind(name=null, (byte[])(pw=null), ctls, null, false); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException("anonymous bind failed: " + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } } else { // Skip LDAP bind for LDAPv3 anonymous bind res = new LdapResult(); res.status = LdapClient.LDAP_SUCCESS; } } else if (authMechanism.equalsIgnoreCase("simple")) { // simple authentication byte[] encodedPw = null; try { encodedPw = encodePassword(pw, isLdapv3); res = ldapBind(name, encodedPw, ctls, null, false); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException("simple bind failed: " + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } finally { // If pw was copied to a new array, clear that array as // a security precaution. if (encodedPw != pw && encodedPw != null) { for (int i = 0; i < encodedPw.length; i++) { encodedPw[i] = 0; } } } } else if (isLdapv3) { // SASL authentication try { res = LdapSasl.saslBind(this, conn, conn.host, name, pw, authMechanism, env, ctls); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException("SASL bind failed: " + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } } else { throw new AuthenticationNotSupportedException(authMechanism); } // // re-try login using v2 if failing over // if (initial && (res.status == LdapClient.LDAP_PROTOCOL_ERROR) && (version == LdapClient.LDAP_VERSION3_VERSION2) && (authMechanism.equalsIgnoreCase("none") || authMechanism.equalsIgnoreCase("anonymous") || authMechanism.equalsIgnoreCase("simple"))) { byte[] encodedPw = null; try { isLdapv3 = false; encodedPw = encodePassword(pw, false); res = ldapBind(name, encodedPw, ctls, null, false); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException(authMechanism + ":" + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } finally { // If pw was copied to a new array, clear that array as // a security precaution. if (encodedPw != pw && encodedPw != null) { for (int i = 0; i < encodedPw.length; i++) { encodedPw[i] = 0; } } } } // principal name not found // (map NameNotFoundException to AuthenticationException) // %%% This is a workaround for Netscape servers returning // %%% no such object when the principal name is not found // %%% Note that when this workaround is applied, it does not allow // %%% response controls to be recorded by the calling context if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) { throw new AuthenticationException( getErrorMessage(res.status, res.errorMessage)); } conn.setV3(isLdapv3); return res; } /** * Sends an LDAP Bind request. * Cannot be private; called by LdapSasl * @param dn The possibly null DN to use in the BIND request. null if anonymous. * @param toServer The possibly null array of bytes to send to the server. * @param auth The authentication mechanism * */ synchronized public LdapResult ldapBind(String dn, byte[]toServer, Control[] bindCtls, String auth, boolean pauseAfterReceipt) throws java.io.IOException, NamingException { ensureOpen(); // flush outstanding requests conn.abandonOutstandingReqs(null); BerEncoder ber = new BerEncoder(); int curMsgId = conn.getMsgId(); LdapResult res = new LdapResult(); res.status = LDAP_OPERATIONS_ERROR; // // build the bind request. // ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); ber.encodeInt(curMsgId); ber.beginSeq(LdapClient.LDAP_REQ_BIND); ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2); ber.encodeString(dn, isLdapv3); // if authentication mechanism specified, it is SASL if (auth != null) { ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3); ber.encodeString(auth, isLdapv3); // SASL mechanism if (toServer != null) { ber.encodeOctetString(toServer, Ber.ASN_OCTET_STR); } ber.endSeq(); } else { if (toServer != null) { ber.encodeOctetString(toServer, Ber.ASN_CONTEXT); } else { ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0); } } ber.endSeq(); // Encode controls if (isLdapv3) { encodeControls(ber, bindCtls); } ber.endSeq(); LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt); if (toServer != null) { ber.reset(); // clear internally-stored password } // Read reply BerDecoder rber = conn.readReply(req); rber.parseSeq(null); // init seq rber.parseInt(); // msg id if (rber.parseByte() != LDAP_REP_BIND) { return res; } rber.parseLength(); parseResult(rber, res, isLdapv3); // handle server's credentials (if present) if (isLdapv3 && (rber.bytesLeft() > 0) && (rber.peekByte() == (Ber.ASN_CONTEXT | 7))) { res.serverCreds = rber.parseOctetString((Ber.ASN_CONTEXT | 7), null); } res.resControls = isLdapv3 ? parseControls(rber) : null; conn.removeRequest(req); return res; } /** * Determines whether SASL encryption/integrity is in progress. * This check is made prior to reauthentication. You cannot reauthenticate * over an encrypted/integrity-protected SASL channel. You must * close the channel and open a new one. */ boolean usingSaslStreams() { return (conn.inStream instanceof SaslInputStream); } synchronized void incRefCount() { ++referenceCount; if (debug > 1) { System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this); } } /** * Returns the encoded password. */ private static byte[] encodePassword(Object pw, boolean v3) throws IOException { if (pw instanceof char[]) { pw = new String((char[])pw); } if (pw instanceof String) { if (v3) { return ((String)pw).getBytes("UTF8"); } else { return ((String)pw).getBytes("8859_1"); } } else { return (byte[])pw; } } synchronized void close(Control[] reqCtls, boolean hardClose) { --referenceCount; if (debug > 1) { System.err.println("LdapClient: " + this); System.err.println("LdapClient: close() called: " + referenceCount); (new Throwable()).printStackTrace(); } if (referenceCount <= 0 && conn != null) { if (debug > 0) System.err.println("LdapClient: closed connection " + this); if (!pooled) { // Not being pooled; continue with closing conn.cleanup(reqCtls, false); conn = null; } else { // Pooled // Is this a real close or a request to return conn to pool if (hardClose) { conn.cleanup(reqCtls, false); conn = null; pcb.removePooledConnection(this); } else { pcb.releasePooledConnection(this); } } } } // NOTE: Should NOT be synchronized otherwise won't be able to close private void forceClose(boolean cleanPool) { referenceCount = 0; // force closing of connection if (debug > 1) { System.err.println("LdapClient: forceClose() of " + this); } if (conn != null) { if (debug > 0) System.err.println( "LdapClient: forced close of connection " + this); conn.cleanup(null, false); conn = null; if (cleanPool) { pcb.removePooledConnection(this); } } } protected void finalize() { if (debug > 0) System.err.println("LdapClient: finalize " + this); forceClose(pooled); } /* * Used by connection pooling to close physical connection. */ synchronized public void closeConnection() { forceClose(false); // this is a pool callback so no need to clean pool } /** * Called by Connection.cleanup(). LdapClient should * notify any unsolicited listeners and removing itself from any pool. * This is almost like forceClose(), except it doesn't call * Connection.cleanup() (because this is called from cleanup()). */ void processConnectionClosure() { // Notify listeners if (unsolicited.size() > 0) { String msg; if (conn != null) { msg = conn.host + ":" + conn.port + " connection closed"; } else { msg = "Connection closed"; } notifyUnsolicited(new CommunicationException(msg)); } // Remove from poolLines 1–500 of 1603. Load more
/* * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap; import java.net.*; import java.io.*; import java.util.Vector; import java.util.Hashtable; import javax.naming.*; import javax.naming.directory.*; import javax.naming.ldap.*; import com.sun.jndi.ldap.pool.PooledConnection; import com.sun.jndi.ldap.pool.PoolCallback; import com.sun.jndi.ldap.sasl.LdapSasl; import com.sun.jndi.ldap.sasl.SaslInputStream; /** * LDAP (RFC-1777) and LDAPv3 (RFC-2251) compliant client * * This class represents a connection to an LDAP client. * Callers interact with this class at an LDAP operation level. * That is, the caller invokes a method to do a SEARCH or MODRDN * operation and gets back the result. * The caller uses the constructor to create a connection to the server. * It then needs to use authenticate() to perform an LDAP BIND. * Note that for v3, BIND is optional so authenticate() might not * actually send a BIND. authenticate() can be used later on to issue * a BIND, for example, for a v3 client that wants to change the connection's * credentials. *<p> * Multiple LdapCtx might share the same LdapClient. For example, contexts * derived from the same initial context would share the same LdapClient * until changes to a context's properties necessitates its own LdapClient. * LdapClient methods that access shared data are thread-safe (i.e., caller * does not have to sync). *<p> * Fields: * isLdapv3 - no sync; initialized and updated within sync authenticate(); * always updated when connection is "quiet" and not shared; * read access from outside LdapClient not sync * referenceCount - sync within LdapClient; exception is forceClose() which * is used by Connection thread to close connection upon receiving * an Unsolicited Notification. * access from outside LdapClient must sync; * conn - no sync; Connection takes care of its own sync * unsolicited - sync Vector; multiple operations sync'ed * * @author Vincent Ryan * @author Jagane Sundar * @author Rosanna Lee */ public final class LdapClient implements PooledConnection { // ---------------------- Constants ---------------------------------- private static final int debug = 0; static final boolean caseIgnore = true; // Default list of binary attributes private static final Hashtable defaultBinaryAttrs = new Hashtable(23,0.75f); static { defaultBinaryAttrs.put("userpassword", Boolean.TRUE); //2.5.4.35 defaultBinaryAttrs.put("javaserializeddata", Boolean.TRUE); //1.3.6.1.4.1.42.2.27.4.1.8 defaultBinaryAttrs.put("javaserializedobject", Boolean.TRUE); // 1.3.6.1.4.1.42.2.27.4.1.2 defaultBinaryAttrs.put("jpegphoto", Boolean.TRUE); //0.9.2342.19200300.100.1.60 defaultBinaryAttrs.put("audio", Boolean.TRUE); //0.9.2342.19200300.100.1.55 defaultBinaryAttrs.put("thumbnailphoto", Boolean.TRUE); //1.3.6.1.4.1.1466.101.120.35 defaultBinaryAttrs.put("thumbnaillogo", Boolean.TRUE); //1.3.6.1.4.1.1466.101.120.36 defaultBinaryAttrs.put("usercertificate", Boolean.TRUE); //2.5.4.36 defaultBinaryAttrs.put("cacertificate", Boolean.TRUE); //2.5.4.37 defaultBinaryAttrs.put("certificaterevocationlist", Boolean.TRUE); //2.5.4.39 defaultBinaryAttrs.put("authorityrevocationlist", Boolean.TRUE); //2.5.4.38 defaultBinaryAttrs.put("crosscertificatepair", Boolean.TRUE); //2.5.4.40 defaultBinaryAttrs.put("photo", Boolean.TRUE); //0.9.2342.19200300.100.1.7 defaultBinaryAttrs.put("personalsignature", Boolean.TRUE); //0.9.2342.19200300.100.1.53 defaultBinaryAttrs.put("x500uniqueidentifier", Boolean.TRUE); //2.5.4.45 } private static final String DISCONNECT_OID = "1.3.6.1.4.1.1466.20036"; // ----------------------- instance fields ------------------------ boolean isLdapv3; // Used by LdapCtx int referenceCount = 1; // Used by LdapCtx for check for sharing Connection conn; // Connection to server; has reader thread // used by LdapCtx for StartTLS final private PoolCallback pcb; final private boolean pooled; private boolean authenticateCalled = false; //////////////////////////////////////////////////////////////////////////// // // constructor: Create an authenticated connection to server // //////////////////////////////////////////////////////////////////////////// LdapClient(String host, int port, String socketFactory, int connectTimeout, int readTimeout, OutputStream trace, PoolCallback pcb) throws NamingException { if (debug > 0) System.err.println("LdapClient: constructor called " + host + ":" + port ); conn = new Connection(this, host, port, socketFactory, connectTimeout, readTimeout, trace); this.pcb = pcb; pooled = (pcb != null); } synchronized boolean authenticateCalled() { return authenticateCalled; } synchronized LdapResult authenticate(boolean initial, String name, Object pw, int version, String authMechanism, Control[] ctls, Hashtable env) throws NamingException { authenticateCalled = true; try { ensureOpen(); } catch (IOException e) { NamingException ne = new CommunicationException(); ne.setRootCause(e); throw ne; } switch (version) { case LDAP_VERSION3_VERSION2: case LDAP_VERSION3: isLdapv3 = true; break; case LDAP_VERSION2: isLdapv3 = false; break; default: throw new CommunicationException("Protocol version " + version + " not supported"); } LdapResult res = null; if (authMechanism.equalsIgnoreCase("none") || authMechanism.equalsIgnoreCase("anonymous")) { // Perform LDAP bind if we are reauthenticating, using LDAPv2, // supporting failover to LDAPv2, or controls have been supplied. if (!initial || (version == LDAP_VERSION2) || (version == LDAP_VERSION3_VERSION2) || ((ctls != null) && (ctls.length > 0))) { try { // anonymous bind; update name/pw for LDAPv2 retry res = ldapBind(name=null, (byte[])(pw=null), ctls, null, false); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException("anonymous bind failed: " + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } } else { // Skip LDAP bind for LDAPv3 anonymous bind res = new LdapResult(); res.status = LdapClient.LDAP_SUCCESS; } } else if (authMechanism.equalsIgnoreCase("simple")) { // simple authentication byte[] encodedPw = null; try { encodedPw = encodePassword(pw, isLdapv3); res = ldapBind(name, encodedPw, ctls, null, false); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException("simple bind failed: " + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } finally { // If pw was copied to a new array, clear that array as // a security precaution. if (encodedPw != pw && encodedPw != null) { for (int i = 0; i < encodedPw.length; i++) { encodedPw[i] = 0; } } } } else if (isLdapv3) { // SASL authentication try { res = LdapSasl.saslBind(this, conn, conn.host, name, pw, authMechanism, env, ctls); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException("SASL bind failed: " + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } } else { throw new AuthenticationNotSupportedException(authMechanism); } // // re-try login using v2 if failing over // if (initial && (res.status == LdapClient.LDAP_PROTOCOL_ERROR) && (version == LdapClient.LDAP_VERSION3_VERSION2) && (authMechanism.equalsIgnoreCase("none") || authMechanism.equalsIgnoreCase("anonymous") || authMechanism.equalsIgnoreCase("simple"))) { byte[] encodedPw = null; try { isLdapv3 = false; encodedPw = encodePassword(pw, false); res = ldapBind(name, encodedPw, ctls, null, false); if (res.status == LdapClient.LDAP_SUCCESS) { conn.setBound(); } } catch (IOException e) { NamingException ne = new CommunicationException(authMechanism + ":" + conn.host + ":" + conn.port); ne.setRootCause(e); throw ne; } finally { // If pw was copied to a new array, clear that array as // a security precaution. if (encodedPw != pw && encodedPw != null) { for (int i = 0; i < encodedPw.length; i++) { encodedPw[i] = 0; } } } } // principal name not found // (map NameNotFoundException to AuthenticationException) // %%% This is a workaround for Netscape servers returning // %%% no such object when the principal name is not found // %%% Note that when this workaround is applied, it does not allow // %%% response controls to be recorded by the calling context if (res.status == LdapClient.LDAP_NO_SUCH_OBJECT) { throw new AuthenticationException( getErrorMessage(res.status, res.errorMessage)); } conn.setV3(isLdapv3); return res; } /** * Sends an LDAP Bind request. * Cannot be private; called by LdapSasl * @param dn The possibly null DN to use in the BIND request. null if anonymous. * @param toServer The possibly null array of bytes to send to the server. * @param auth The authentication mechanism * */ synchronized public LdapResult ldapBind(String dn, byte[]toServer, Control[] bindCtls, String auth, boolean pauseAfterReceipt) throws java.io.IOException, NamingException { ensureOpen(); // flush outstanding requests conn.abandonOutstandingReqs(null); BerEncoder ber = new BerEncoder(); int curMsgId = conn.getMsgId(); LdapResult res = new LdapResult(); res.status = LDAP_OPERATIONS_ERROR; // // build the bind request. // ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); ber.encodeInt(curMsgId); ber.beginSeq(LdapClient.LDAP_REQ_BIND); ber.encodeInt(isLdapv3 ? LDAP_VERSION3 : LDAP_VERSION2); ber.encodeString(dn, isLdapv3); // if authentication mechanism specified, it is SASL if (auth != null) { ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3); ber.encodeString(auth, isLdapv3); // SASL mechanism if (toServer != null) { ber.encodeOctetString(toServer, Ber.ASN_OCTET_STR); } ber.endSeq(); } else { if (toServer != null) { ber.encodeOctetString(toServer, Ber.ASN_CONTEXT); } else { ber.encodeOctetString(null, Ber.ASN_CONTEXT, 0, 0); } } ber.endSeq(); // Encode controls if (isLdapv3) { encodeControls(ber, bindCtls); } ber.endSeq(); LdapRequest req = conn.writeRequest(ber, curMsgId, pauseAfterReceipt); if (toServer != null) { ber.reset(); // clear internally-stored password } // Read reply BerDecoder rber = conn.readReply(req); rber.parseSeq(null); // init seq rber.parseInt(); // msg id if (rber.parseByte() != LDAP_REP_BIND) { return res; } rber.parseLength(); parseResult(rber, res, isLdapv3); // handle server's credentials (if present) if (isLdapv3 && (rber.bytesLeft() > 0) && (rber.peekByte() == (Ber.ASN_CONTEXT | 7))) { res.serverCreds = rber.parseOctetString((Ber.ASN_CONTEXT | 7), null); } res.resControls = isLdapv3 ? parseControls(rber) : null; conn.removeRequest(req); return res; } /** * Determines whether SASL encryption/integrity is in progress. * This check is made prior to reauthentication. You cannot reauthenticate * over an encrypted/integrity-protected SASL channel. You must * close the channel and open a new one. */ boolean usingSaslStreams() { return (conn.inStream instanceof SaslInputStream); } synchronized void incRefCount() { ++referenceCount; if (debug > 1) { System.err.println("LdapClient.incRefCount: " + referenceCount + " " + this); } } /** * Returns the encoded password. */ private static byte[] encodePassword(Object pw, boolean v3) throws IOException { if (pw instanceof char[]) { pw = new String((char[])pw); } if (pw instanceof String) { if (v3) { return ((String)pw).getBytes("UTF8"); } else { return ((String)pw).getBytes("8859_1"); } } else { return (byte[])pw; } } synchronized void close(Control[] reqCtls, boolean hardClose) { --referenceCount; if (debug > 1) { System.err.println("LdapClient: " + this); System.err.println("LdapClient: close() called: " + referenceCount); (new Throwable()).printStackTrace(); } if (referenceCount <= 0 && conn != null) { if (debug > 0) System.err.println("LdapClient: closed connection " + this); if (!pooled) { // Not being pooled; continue with closing conn.cleanup(reqCtls, false); conn = null; } else { // Pooled // Is this a real close or a request to return conn to pool if (hardClose) { conn.cleanup(reqCtls, false); conn = null; pcb.removePooledConnection(this); } else { pcb.releasePooledConnection(this); } } } } // NOTE: Should NOT be synchronized otherwise won't be able to close private void forceClose(boolean cleanPool) { referenceCount = 0; // force closing of connection if (debug > 1) { System.err.println("LdapClient: forceClose() of " + this); } if (conn != null) { if (debug > 0) System.err.println( "LdapClient: forced close of connection " + this); conn.cleanup(null, false); conn = null; if (cleanPool) { pcb.removePooledConnection(this); } } } protected void finalize() { if (debug > 0) System.err.println("LdapClient: finalize " + this); forceClose(pooled); } /* * Used by connection pooling to close physical connection. */ synchronized public void closeConnection() { forceClose(false); // this is a pool callback so no need to clean pool } /** * Called by Connection.cleanup(). LdapClient should * notify any unsolicited listeners and removing itself from any pool. * This is almost like forceClose(), except it doesn't call * Connection.cleanup() (because this is called from cleanup()). */ void processConnectionClosure() { // Notify listeners if (unsolicited.size() > 0) { String msg; if (conn != null) { msg = conn.host + ":" + conn.port + " connection closed"; } else { msg = "Connection closed"; } notifyUnsolicited(new CommunicationException(msg)); } // Remove from pool