mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
14.09.2012 1dfff197eadcf24823d7915e6eead2a850f679f9
Fix OPENDJ-420: Rare SSLExceptions while handling LDAPS connections and big LDAP searches

Re-implement SASL and TLS byte channel support including:

* handle SSL application and packet buffer size changes
* introduce finer grained locking so that reads and writes can be performed concurrently
* handle incoming SASL packets which are bigger than the negotiated buffer size.

9 files modified
1496 ■■■■ changed files
opends/src/server/org/opends/server/api/ClientConnection.java 21 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java 18 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java 14 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java 15 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/SASLByteChannel.java 579 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/SASLContext.java 63 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/TLSByteChannel.java 737 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java 47 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/ClientConnection.java
@@ -23,13 +23,14 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2012 ForgeRock AS
 */
package org.opends.server.api;
import java.net.InetAddress;
import java.nio.channels.ByteChannel;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Collection;
@@ -47,7 +48,6 @@
import org.opends.server.core.PluginConfigManager;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.extensions.RedirectingByteChannel;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -1327,7 +1327,7 @@
   *
   * @return The lowest level channel associated with a connection.
   */
  public RedirectingByteChannel getChannel() {
  public ByteChannel getChannel() {
    // By default, return null, which indicates that there should
    // be no channel.  Subclasses should override this if
    // they want to support a channel.
@@ -1351,21 +1351,6 @@
  /**
   * Return the largest application buffer size that should be used
   * for a connection.
   *
   * @return The application buffer size.
   */
  public int getAppBufferSize() {
    // By default, return 0, which indicates that there should
    // be no application buffer size.  Subclasses should override
    //this if they want to support a application buffer size.
    return 0;
  }
  /**
   * Retrieves the size limit that will be enforced for searches
   * performed using this client connection.
   *
opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java
@@ -42,15 +42,6 @@
{
  /**
   * Return a buffer size of the byte channel.
   *
   * @return Integer representing the byte channel application buffer size.
   */
  int getAppBufSize();
  /**
   * Return a certificate chain array.
   *
   * @return A certificate chain array.
@@ -87,12 +78,9 @@
  /**
   * Factory method: creates a new security ByteChannel layer wrapping the
   * provided ByteChannel.
   * Returns the security provider's byte channel.
   *
   * @param channel
   *          The byte channel to be wrapped.
   * @return A byte channel wrapping the specified byte channel.
   * @return The security provider's byte channel.
   */
  ByteChannel wrapChannel(ByteChannel channel);
  ByteChannel getChannel();
}
opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions copyright 2012 ForgeRock AS.
 */
package org.opends.server.extensions;
@@ -163,18 +164,7 @@
         (SASLContext) clientConn.getSASLAuthStateInfo();
      if(saslContext == null) {
          try {
            //If the connection is secure already (i.e., TLS), then make the
            //receive buffers sizes match.
            if(clientConn.isSecure()) {
              HashMap<String, String>secProps =
                                      new HashMap<String,String>(saslProps);
              int maxBuf = clientConn.getAppBufferSize();
              secProps.put(Sasl.MAX_BUFFER, Integer.toString(maxBuf));
              saslContext = SASLContext.createSASLContext(secProps,
                                      serverFQDN, SASL_MECHANISM_DIGEST_MD5,
                                      identityMapper);
            } else
              saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
            saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
                            SASL_MECHANISM_DIGEST_MD5, identityMapper);
          } catch (SaslException ex) {
              if (debugEnabled()) {
opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -23,7 +23,7 @@
 *
 *
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 *      Portions Copyright 2011 ForgeRock AS
 *      Portions Copyright 2011-2012 ForgeRock AS
 */
package org.opends.server.extensions;
@@ -398,19 +398,8 @@
    SASLContext saslContext = (SASLContext) clientConn.getSASLAuthStateInfo();
    if (saslContext == null) {
      try {
        //If the connection is secure already (i.e., TLS), then make the
        //receive buffers sizes match.
        if(clientConn.isSecure()) {
          HashMap<String, String>secProps =
                                  new HashMap<String,String>(saslProps);
          int maxBuf = clientConn.getAppBufferSize();
          secProps.put(Sasl.MAX_BUFFER, Integer.toString(maxBuf));
          saslContext = SASLContext.createSASLContext(secProps, serverFQDN,
        saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
                                  SASL_MECHANISM_GSSAPI, identityMapper);
        } else {
          saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
                                  SASL_MECHANISM_GSSAPI, identityMapper);
        }
      } catch (SaslException ex) {
        if (debugEnabled())
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
@@ -143,7 +143,7 @@
   */
  public final void redirect(final ConnectionSecurityProvider provider)
  {
    redirect = provider.wrapChannel(child);
    redirect = provider.getChannel();
  }
opends/src/server/org/opends/server/extensions/SASLByteChannel.java
@@ -35,9 +35,6 @@
import java.nio.channels.ByteChannel;
import java.security.cert.Certificate;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslException;
import org.opends.server.api.ClientConnection;
@@ -46,10 +43,229 @@
 * This class implements a SASL byte channel that can be used during
 * confidentiality and integrity.
 */
public class SASLByteChannel implements ByteChannel, ConnectionSecurityProvider
public final class SASLByteChannel implements ConnectionSecurityProvider
{
  /**
   * Private implementation.
   */
  private final class ByteChannelImpl implements ByteChannel
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() throws IOException
    {
      synchronized (readLock)
      {
        synchronized (writeLock)
        {
          saslContext.dispose();
          channel.close();
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isOpen()
    {
      return saslContext != null;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public int read(final ByteBuffer unwrappedData) throws IOException
    {
      synchronized (readLock)
      {
        // Only read and unwrap new data if needed.
        if (!recvUnwrappedBuffer.hasRemaining())
        {
          final int read = doRecvAndUnwrap();
          if (read <= 0)
          {
            // No data read or end of stream.
            return read;
          }
        }
        // Copy available data.
        final int startPos = unwrappedData.position();
        if (recvUnwrappedBuffer.remaining() > unwrappedData.remaining())
        {
          // Unwrapped data does not fit in client buffer so copy one byte at a
          // time: it's annoying that there is no easy way to do this with
          // ByteBuffers.
          while (unwrappedData.hasRemaining())
          {
            unwrappedData.put(recvUnwrappedBuffer.get());
          }
        }
        else
        {
          // Unwrapped data fits client buffer so block copy.
          unwrappedData.put(recvUnwrappedBuffer);
        }
        return unwrappedData.position() - startPos;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public int write(final ByteBuffer unwrappedData) throws IOException
    {
      // This method will block until the entire message is sent.
      final int bytesWritten = unwrappedData.remaining();
      // Synchronized in order to prevent interleaving and reordering.
      synchronized (writeLock)
      {
        // Write data in sendBufferSize segments.
        while (unwrappedData.hasRemaining())
        {
          final int remaining = unwrappedData.remaining();
          final int wrapSize = (remaining < sendUnwrappedBufferSize) ? remaining
              : sendUnwrappedBufferSize;
          final byte[] wrappedDataBytes;
          if (unwrappedData.hasArray())
          {
            // Avoid extra copy if ByteBuffer is array based.
            wrappedDataBytes = saslContext.wrap(unwrappedData.array(),
                unwrappedData.arrayOffset(), wrapSize);
            unwrappedData.position(unwrappedData.position() + wrapSize);
          }
          else
          {
            // Non-array based ByteBuffer, so copy.
            unwrappedData.get(sendUnwrappedBytes, 0, wrapSize);
            wrappedDataBytes = saslContext
                .wrap(sendUnwrappedBytes, 0, wrapSize);
            unwrappedData.position(unwrappedData.position() + wrapSize);
          }
          // Encode SASL packet: 4 byte length + wrapped data.
          if (sendWrappedBuffer.capacity() < wrappedDataBytes.length + 4)
          {
            // Resize the send buffer.
            sendWrappedBuffer = ByteBuffer
                .allocate(wrappedDataBytes.length + 4);
          }
          sendWrappedBuffer.clear();
          sendWrappedBuffer.putInt(wrappedDataBytes.length);
          sendWrappedBuffer.put(wrappedDataBytes);
          sendWrappedBuffer.flip();
          // Write the SASL packet: our IO stack will block until all the data
          // is written.
          channel.write(sendWrappedBuffer);
        }
      }
      return bytesWritten;
    }
    // Attempt to read and unwrap the next SASL packet.
    private int doRecvAndUnwrap() throws IOException
    {
      // Read the encoded packet length first.
      if (recvWrappedLength < 0)
      {
        final int read = channel.read(recvWrappedLengthBuffer);
        if (read <= 0)
        {
          // No data read or end of stream.
          return read;
        }
        if (recvWrappedLengthBuffer.hasRemaining())
        {
          // Unable to read the length, so no data available yet.
          return 0;
        }
        // Decode the length and reset the length buffer.
        recvWrappedLengthBuffer.flip();
        recvWrappedLength = recvWrappedLengthBuffer.getInt();
        recvWrappedLengthBuffer.clear();
        // Check that the length is valid.
        if (recvWrappedLength > recvWrappedBufferMaximumSize)
        {
          throw new IOException(
              "Client sent a SASL packet specifying a length "
                  + recvWrappedLength
                  + " which exceeds the negotiated limit of "
                  + recvWrappedBufferMaximumSize);
        }
        if (recvWrappedLength < 0)
        {
          throw new IOException(
              "Client sent a SASL packet specifying a negative length "
                  + recvWrappedLength);
        }
        // Prepare the recv buffer for reading.
        recvWrappedBuffer.clear();
        recvWrappedBuffer.limit(recvWrappedLength);
      }
      // Read wrapped data.
      final int read = channel.read(recvWrappedBuffer);
      if (read <= 0)
      {
        // No data read or end of stream.
        return read;
      }
      if (recvWrappedBuffer.hasRemaining())
      {
        // Unable to read the full packet, so no data available yet.
        return 0;
      }
      // The complete packet has been read, so unwrap it.
      recvWrappedBuffer.flip();
      final byte[] unwrappedDataBytes = saslContext.unwrap(
          recvWrappedBuffer.array(), 0, recvWrappedLength);
      recvWrappedLength = -1;
      if (recvUnwrappedBuffer.capacity() < unwrappedDataBytes.length)
      {
        // Resize the recv buffer (this shouldn't ever happen).
        recvUnwrappedBuffer = ByteBuffer.allocate(unwrappedDataBytes.length);
      }
      recvUnwrappedBuffer.clear();
      recvUnwrappedBuffer.put(unwrappedDataBytes);
      recvUnwrappedBuffer.flip();
      return recvUnwrappedBuffer.remaining();
    }
  }
  /**
   * Return a SASL byte channel instance created using the specified parameters.
   *
   * @param c
@@ -68,31 +284,23 @@
  // The SASL context associated with the provider
  private SASLContext saslContext;
  // The byte channel associated with this provider.
  private final RedirectingByteChannel channel;
  // The number of bytes in the length buffer.
  private static final int lengthSize = 4;
  // Length of the buffer.
  private int bufLength;
  // The SASL mechanism name.
  private final String name;
  private final ByteChannel channel;
  private final ByteChannelImpl pimpl = new ByteChannelImpl();
  private final SASLContext saslContext;
  // Buffers used in reading and decoding (unwrap)
  private final ByteBuffer readBuffer, decodeBuffer;
  private ByteBuffer recvUnwrappedBuffer;
  private final ByteBuffer recvWrappedBuffer;
  private final int recvWrappedBufferMaximumSize;
  private int recvWrappedLength = -1;
  private final ByteBuffer recvWrappedLengthBuffer = ByteBuffer.allocate(4);
  // How many bytes of the subsequent buffer is needed to complete a partially
  // read buffer.
  private int neededBytes = 0;
  private final int sendUnwrappedBufferSize;
  private final byte[] sendUnwrappedBytes;
  private ByteBuffer sendWrappedBuffer;
  // Used to not reset the buffer length size because the first 4 bytes of a
  // buffer are not size bytes.
  private boolean reading = false;
  private final Object readLock = new Object();
  private final Object writeLock = new Object();
@@ -112,10 +320,16 @@
  {
    this.name = name;
    this.saslContext = saslContext;
    this.channel = connection.getChannel();
    this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize());
    this.decodeBuffer = ByteBuffer.allocate(connection.getAppBufferSize()
        + lengthSize);
    channel = connection.getChannel();
    recvWrappedBufferMaximumSize = saslContext.getMaxReceiveBufferSize();
    sendUnwrappedBufferSize = saslContext.getMaxRawSendBufferSize();
    recvWrappedBuffer = ByteBuffer.allocate(recvWrappedBufferMaximumSize);
    recvUnwrappedBuffer = ByteBuffer.allocate(recvWrappedBufferMaximumSize);
    recvUnwrappedBuffer.flip(); // Initially nothing has been received.
    sendUnwrappedBytes = new byte[sendUnwrappedBufferSize];
    sendWrappedBuffer = ByteBuffer.allocate(sendUnwrappedBufferSize + 64);
  }
@@ -124,21 +338,9 @@
   * {@inheritDoc}
   */
  @Override
  public synchronized void close() throws IOException
  public ByteChannel getChannel()
  {
    saslContext.dispose();
    saslContext = null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public int getAppBufSize()
  {
    return saslContext.getBufSize(Sasl.MAX_BUFFER);
    return pimpl;
  }
@@ -180,300 +382,9 @@
   * {@inheritDoc}
   */
  @Override
  public boolean isOpen()
  {
    return saslContext != null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSecure()
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public synchronized int read(final ByteBuffer clearDst) throws IOException
  {
    int bytesToRead = lengthSize;
    if (reading)
    {
      bytesToRead = neededBytes;
    }
    final int readResult = readAll(readBuffer, bytesToRead);
    if (readResult == -1)
    {
      return -1;
    }
    // The previous buffer read was not complete, the current
    // buffer completes it.
    if (neededBytes > 0 && readResult > 0)
    {
      return (processPartial(readResult, clearDst));
    }
    if (readResult == 0 && !reading)
    {
      return 0;
    }
    if (!reading)
    {
      bufLength = getBufLength(readBuffer);
    }
    reading = false;
    // The buffer length is greater than what is there, save what is there,
    // figure out how much more is needed and return.
    if (bufLength > readBuffer.position())
    {
      neededBytes = bufLength - readBuffer.position() + lengthSize;
      readBuffer.flip();
      decodeBuffer.put(readBuffer);
      readBuffer.clear();
      return 0;
    }
    else
    {
      readBuffer.flip();
      decodeBuffer.put(readBuffer);
      final byte[] inBytes = decodeBuffer.array();
      final byte[] clearBytes = saslContext.unwrap(inBytes, lengthSize,
          bufLength);
      decodeBuffer.clear();
      clearDst.put(clearBytes);
      readBuffer.clear();
      return clearDst.position();
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ByteChannel wrapChannel(final ByteChannel channel)
  {
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public synchronized int write(final ByteBuffer clearSrc) throws IOException
  {
    final int sendBufSize = getAppBufSize();
    final int srcLen = clearSrc.remaining();
    final ByteBuffer sendBuffer = ByteBuffer.allocate(sendBufSize);
    if (srcLen > sendBufSize)
    {
      final int oldPos = clearSrc.position();
      int curPos = oldPos;
      int curLimit = oldPos + sendBufSize;
      while (curPos < srcLen)
      {
        clearSrc.position(curPos);
        clearSrc.limit(curLimit);
        sendBuffer.put(clearSrc);
        writeChannel(wrap(sendBuffer.array(), clearSrc.remaining()));
        curPos = curLimit;
        curLimit = Math.min(srcLen, curPos + sendBufSize);
      }
      return srcLen;
    }
    else
    {
      sendBuffer.put(clearSrc);
      return writeChannel(wrap(sendBuffer.array(), srcLen));
    }
  }
  /**
   * Return the clear buffer length as determined by processing the first 4
   * bytes of the specified buffer.
   *
   * @param byteBuf
   *          The buffer to examine the first 4 bytes of.
   * @return The size of the clear buffer.
   */
  private int getBufLength(final ByteBuffer byteBuf)
  {
    int answer = 0;
    for (int i = 0; i < lengthSize; i++)
    {
      final byte b = byteBuf.get(i);
      answer <<= 8;
      answer |= (b & 0xff);
    }
    return answer;
  }
  /**
   * Finish processing a previous, partially read buffer using some, or, all of
   * the bytes of the current buffer.
   */
  private int processPartial(final int readResult, final ByteBuffer clearDst)
      throws IOException
  {
    readBuffer.flip();
    // Use all of the bytes of the current buffer and read some more.
    if (neededBytes > readResult)
    {
      neededBytes -= readResult;
      decodeBuffer.put(readBuffer);
      readBuffer.clear();
      reading = false;
      return 0;
    }
    // Use a portion of the current buffer.
    for (; neededBytes > 0; neededBytes--)
    {
      decodeBuffer.put(readBuffer.get());
    }
    // Unwrap the now completed buffer.
    final byte[] inBytes = decodeBuffer.array();
    final byte[] clearBytes = saslContext
        .unwrap(inBytes, lengthSize, bufLength);
    clearDst.put(clearBytes);
    decodeBuffer.clear();
    readBuffer.compact();
    // If the read buffer has bytes, these are a new buffer. Reset the
    // buffer length to the new value.
    if (readBuffer.position() != 0)
    {
      bufLength = getBufLength(readBuffer);
      reading = true;
    }
    else
    {
      reading = false;
    }
    return clearDst.position();
  }
  /**
   * Read from the socket channel into the specified byte buffer at least the
   * number of bytes specified in the total parameter.
   *
   * @param byteBuf
   *          The byte buffer to put the bytes in.
   * @param total
   *          The total number of bytes to read from the socket channel.
   * @return The number of bytes read, 0 or -1.
   * @throws IOException
   *           If an error occurred reading the socket channel.
   */
  private int readAll(final ByteBuffer byteBuf, int total) throws IOException
  {
    while (channel.isOpen() && total > 0)
    {
      final int count = channel.read(byteBuf);
      if (count == -1)
      {
        return -1;
      }
      if (count == 0)
      {
        return 0;
      }
      total -= count;
    }
    if (total > 0)
    {
      return -1;
    }
    else
    {
      return byteBuf.position();
    }
  }
  /**
   * Creates a buffer suitable to send to the client using the specified clear
   * byte array and length of the bytes to wrap.
   *
   * @param clearBytes
   *          The clear byte array to send to the client.
   * @param len
   *          The length of the bytes to wrap in the byte array.
   * @throws SaslException
   *           If the wrap of the bytes fails.
   */
  private ByteBuffer wrap(final byte[] clearBytes, final int len)
      throws SaslException
  {
    final byte[] wrapBytes = saslContext.wrap(clearBytes, 0, len);
    final byte[] outBytes = new byte[wrapBytes.length + lengthSize];
    writeBufLen(outBytes, wrapBytes.length);
    System.arraycopy(wrapBytes, 0, outBytes, lengthSize, wrapBytes.length);
    return ByteBuffer.wrap(outBytes);
  }
  /**
   * Writes the specified len parameter into the buffer in a form that can be
   * sent over a network to the client.
   *
   * @param buf
   *          The buffer to hold the length bytes.
   * @param len
   *          The length to encode.
   */
  private void writeBufLen(final byte[] buf, int len)
  {
    for (int i = 3; i >= 0; i--)
    {
      buf[i] = (byte) (len & 0xff);
      len >>>= 8;
    }
  }
  /**
   * Write the specified byte buffer to the socket channel.
   *
   * @param buffer
   *          The byte buffer to write to the socket channel.
   * @return {@code true} if the byte buffer was successfully written to the
   *         socket channel, or, {@code false} if not.
   */
  private int writeChannel(final ByteBuffer buffer) throws IOException
  {
    return channel.write(buffer);
  }
}
opends/src/server/org/opends/server/extensions/SASLContext.java
@@ -447,16 +447,65 @@
  /**
   * Return the negotiated buffer size.
   * Returns the negotiated maximum size of protected data which can be received
   * from the client.
   *
   * @param prop
   *          The buffer size property to return.
   * @return The value of the negotiated buffer size.
   * @return The negotiated maximum size of protected data which can be received
   *         from the client.
   */
  int getBufSize(final String prop)
  int getMaxReceiveBufferSize()
  {
    final String sizeStr = (String) saslServer.getNegotiatedProperty(prop);
    return Integer.parseInt(sizeStr);
    String str = (String) saslServer.getNegotiatedProperty(Sasl.MAX_BUFFER);
    if (str != null)
    {
      try
      {
        return Integer.parseInt(str);
      }
      catch (NumberFormatException e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
      }
    }
    // Default buffer size if not specified according to Java SASL
    // documentation.
    return 65536;
  }
  /**
   * Returns the negotiated maximum size of raw data which can be sent to the
   * client.
   *
   * @return The negotiated maximum size of raw data which can be sent to the
   *         client.
   */
  int getMaxRawSendBufferSize()
  {
    String str = (String) saslServer.getNegotiatedProperty(Sasl.RAW_SEND_SIZE);
    if (str != null)
    {
      try
      {
        return Integer.parseInt(str);
      }
      catch (NumberFormatException e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
      }
    }
    // Default buffer size if not specified according to Java SASL
    // documentation.
    return 65536;
  }
opends/src/server/org/opends/server/extensions/TLSByteChannel.java
@@ -42,6 +42,7 @@
import java.util.Set;
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
import org.opends.server.api.ClientConnection;
@@ -53,50 +54,375 @@
/**
 * A class that provides a TLS byte channel implementation.
 */
public class TLSByteChannel implements ByteChannel, ConnectionSecurityProvider
public final class TLSByteChannel implements ConnectionSecurityProvider
{
  private static final DebugTracer TRACER = getTracer();
  /**
   * Private implementation.
   */
  private final class ByteChannelImpl implements ByteChannel
  {
  private final ByteChannel socketChannel;
    /**
     * {@inheritDoc}
     */
    public void close() throws IOException
    {
      synchronized (readLock)
      {
        synchronized (writeLock)
        {
          final boolean isInitiator = !sslEngine.isInboundDone();
  private final SSLEngine sslEngine;
          try
          {
            if (!sslEngine.isOutboundDone())
            {
              sslEngine.closeOutbound();
              while (doWrapAndSend(EMPTY_BUFFER) > 0)
              {
                // Write out any remaining SSL close notifications.
              }
            }
          }
          catch (final ClosedChannelException e)
          {
            // Ignore this so that close is idempotent.
          }
          finally
          {
            try
            {
              sslEngine.closeInbound();
            }
            catch (final SSLException e)
            {
              // Not yet received peer's close notification. Ignore this if we
              // are the initiator.
              if (!isInitiator)
              {
                throw e;
              }
            }
            finally
            {
              channel.close();
            }
          }
        }
      }
    }
  // read copy to buffer
  private final ByteBuffer appData;
  // read encrypted
  private final ByteBuffer appNetData;
  // Write encrypted
  private final ByteBuffer netData;
  private final ByteBuffer tempData;
    /**
     * {@inheritDoc}
     */
    public boolean isOpen()
    {
      return !sslEngine.isOutboundDone() || !sslEngine.isInboundDone();
    }
  private final int sslBufferSize;
  private final int appBufSize;
  private boolean reading = false;
    /**
     * {@inheritDoc}
     */
    public int read(final ByteBuffer unwrappedData) throws IOException
    {
      synchronized (readLock)
      {
        // Repeat until there is some unwrapped data available or all available
        // data has been read from the underlying socket.
        if (!recvUnwrappedBuffer.hasRemaining())
        {
          final int read = doRecvAndUnwrap();
          if (read <= 0)
          {
            // No data read or end of stream.
            return read;
          }
        }
        // Copy available data.
        final int startPos = unwrappedData.position();
        if (recvUnwrappedBuffer.remaining() > unwrappedData.remaining())
        {
          // Unwrapped data does not fit in client buffer so copy one by at a
          // time: it's annoying that there is no easy way to do this with
          // ByteBuffers.
          while (unwrappedData.hasRemaining())
          {
            unwrappedData.put(recvUnwrappedBuffer.get());
          }
        }
        else
        {
          // Unwrapped data fits client buffer so block copy.
          unwrappedData.put(recvUnwrappedBuffer);
        }
        return unwrappedData.position() - startPos;
      }
    }
    /**
     * {@inheritDoc}
     */
    public int write(final ByteBuffer unwrappedData) throws IOException
    {
      // This method will block until the entire message is sent.
      final int bytesWritten = unwrappedData.remaining();
      // Synchronized in order to prevent interleaving and reordering.
      synchronized (writeLock)
      {
        // Repeat until the entire input data is written.
        while (unwrappedData.hasRemaining())
        {
          // Wrap and send the data.
          doWrapAndSend(unwrappedData);
          // Perform handshake if needed.
          if (isHandshaking(sslEngine.getHandshakeStatus()))
          {
            doHandshake(false /* isReading */);
          }
        }
      }
      return bytesWritten;
    }
    private void doHandshake(final boolean isReading) throws IOException
    {
      // This lock is probably unnecessary since tasks can be run in parallel,
      // but it adds no additional overhead so there's little harm in having
      // it.
      synchronized (handshakeLock)
      {
        while (true)
        {
          switch (sslEngine.getHandshakeStatus())
          {
          case NEED_TASK:
            Runnable runnable;
            while ((runnable = sslEngine.getDelegatedTask()) != null)
            {
              runnable.run();
            }
            break;
          case NEED_UNWRAP:
            // Block for writes, but be non-blocking for reads.
            if (isReading)
            {
              // Let doRecvAndUnwrap() deal with this.
              return;
            }
            // Need to do an unwrap (read) while writing.
            if (doRecvAndUnwrap() < 0)
            {
              throw new ClosedChannelException();
            }
            break;
          case NEED_WRAP:
            doWrapAndSend(EMPTY_BUFFER);
            break;
          default: // NOT_HANDSHAKING, FINISHED.
            return;
          }
        }
      }
    }
    // Attempt to read and unwrap the next SSL packet.
    private int doRecvAndUnwrap() throws IOException
    {
      // Synchronize SSL unwrap with channel reads.
      synchronized (unwrapLock)
      {
        // Repeat if there is underflow or overflow.
        boolean needRead = true;
        while (true)
        {
          // Read wrapped data if needed.
          if (needRead)
          {
            recvWrappedBuffer.compact(); // Prepare for append.
            final int read = channel.read(recvWrappedBuffer);
            recvWrappedBuffer.flip(); // Restore for read.
            if (read < 0)
            {
              // Peer abort?
              sslEngine.closeInbound();
              return -1;
            }
          }
          else
          {
            needRead = true;
          }
          // Unwrap.
          recvUnwrappedBuffer.compact(); // Prepare for append.
          final SSLEngineResult result = sslEngine.unwrap(recvWrappedBuffer,
              recvUnwrappedBuffer);
          recvUnwrappedBuffer.flip(); // Restore for read.
          switch (result.getStatus())
          {
          case BUFFER_OVERFLOW:
            // The unwrapped buffer is not big enough: resize and repeat.
            final int newAppSize = sslEngine.getSession()
                .getApplicationBufferSize();
            final ByteBuffer newRecvUnwrappedBuffer = ByteBuffer
                .allocate(recvUnwrappedBuffer.limit() + newAppSize);
            newRecvUnwrappedBuffer.put(recvUnwrappedBuffer);
            newRecvUnwrappedBuffer.flip();
            recvUnwrappedBuffer = newRecvUnwrappedBuffer;
            needRead = false;
            break; // Retry unwrap.
          case BUFFER_UNDERFLOW:
            // Not enough data was read. This either means that the inbound
            // buffer was too small, or not enough data is available.
            final int newPktSize = sslEngine.getSession().getPacketBufferSize();
            if (newPktSize > recvWrappedBuffer.capacity())
            {
              // Buffer needs resizing.
              final ByteBuffer newRecvWrappedBuffer = ByteBuffer
                  .allocate(newPktSize);
              newRecvWrappedBuffer.put(recvWrappedBuffer);
              newRecvWrappedBuffer.flip();
              recvWrappedBuffer = newRecvWrappedBuffer;
              break;
            }
            else
            {
              // Not enough data is available to read a complete SSL packet.
              return 0;
            }
          case CLOSED:
            // Peer sent SSL close notification.
            sslEngine.closeInbound();
            return -1;
          default: // OK
            if (recvUnwrappedBuffer.hasRemaining())
            {
              // Some application data was read so return it.
              return recvUnwrappedBuffer.remaining();
            }
            else if (isHandshaking(result.getHandshakeStatus()))
            {
              // No application data was read, but if we are handshaking then
              // try to continue.
              if (result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP
                  && !recvWrappedBuffer.hasRemaining())
              {
                // Not enough data is available to continue handshake.
                return 0;
              }
              else
              {
                // Continue handshake.
                doHandshake(true /* isReading */);
              }
            }
            else
            {
              // No data available and not handshaking.
              return 0;
            }
          }
        }
      }
    }
    // Attempt to wrap and send the next SSL packet.
    private int doWrapAndSend(final ByteBuffer unwrappedData)
        throws IOException
    {
      // Synchronize SSL wrap with channel writes.
      synchronized (wrapLock)
      {
        // Repeat while there is overflow.
        while (true)
        {
          final SSLEngineResult result = sslEngine.wrap(unwrappedData,
              sendWrappedBuffer);
          switch (result.getStatus())
          {
          case BUFFER_OVERFLOW:
            // The wrapped buffer is not big enough: resize and repeat.
            final int newSize = sslEngine.getSession().getPacketBufferSize();
            final ByteBuffer newSendWrappedBuffer = ByteBuffer
                .allocate(sendWrappedBuffer.position() + newSize);
            sendWrappedBuffer.flip();
            newSendWrappedBuffer.put(sendWrappedBuffer);
            sendWrappedBuffer = newSendWrappedBuffer;
            break; // Retry.
          case BUFFER_UNDERFLOW:
            // This should not happen for sends.
            throw new SSLException("Got unexpected underflow while wrapping");
          case CLOSED:
            throw new ClosedChannelException();
          default: // OK
            // Write the SSL packet: our IO stack will block until all the
            // data is written.
            sendWrappedBuffer.flip();
            while (sendWrappedBuffer.hasRemaining())
            {
              channel.write(sendWrappedBuffer);
            }
            final int written = sendWrappedBuffer.position();
            sendWrappedBuffer.clear();
            return written;
          }
        }
      }
    }
    private boolean isHandshaking(final HandshakeStatus status)
    {
      return status != HandshakeStatus.NOT_HANDSHAKING;
    }
  }
  // Map of cipher phrases to effective key size (bits). Taken from the
  // following RFCs: 5289, 4346, 3268,4132 and 4162.
  private static final Map<String, Integer> cipherMap;
  private static final Map<String, Integer> CIPHER_MAP;
  static
  {
    cipherMap = new LinkedHashMap<String, Integer>();
    cipherMap.put("_WITH_AES_256_CBC_", new Integer(256));
    cipherMap.put("_WITH_CAMELLIA_256_CBC_", new Integer(256));
    cipherMap.put("_WITH_AES_256_GCM_", new Integer(256));
    cipherMap.put("_WITH_3DES_EDE_CBC_", new Integer(112));
    cipherMap.put("_WITH_AES_128_GCM_", new Integer(128));
    cipherMap.put("_WITH_SEED_CBC_", new Integer(128));
    cipherMap.put("_WITH_CAMELLIA_128_CBC_", new Integer(128));
    cipherMap.put("_WITH_AES_128_CBC_", new Integer(128));
    cipherMap.put("_WITH_IDEA_CBC_", new Integer(128));
    cipherMap.put("_WITH_DES_CBC_", new Integer(56));
    cipherMap.put("_WITH_RC2_CBC_40_", new Integer(40));
    cipherMap.put("_WITH_RC4_40_", new Integer(40));
    cipherMap.put("_WITH_DES40_CBC_", new Integer(40));
    cipherMap.put("_WITH_NULL_", new Integer(0));
  };
    CIPHER_MAP = new LinkedHashMap<String, Integer>();
    CIPHER_MAP.put("_WITH_AES_256_CBC_", new Integer(256));
    CIPHER_MAP.put("_WITH_CAMELLIA_256_CBC_", new Integer(256));
    CIPHER_MAP.put("_WITH_AES_256_GCM_", new Integer(256));
    CIPHER_MAP.put("_WITH_3DES_EDE_CBC_", new Integer(112));
    CIPHER_MAP.put("_WITH_AES_128_GCM_", new Integer(128));
    CIPHER_MAP.put("_WITH_SEED_CBC_", new Integer(128));
    CIPHER_MAP.put("_WITH_CAMELLIA_128_CBC_", new Integer(128));
    CIPHER_MAP.put("_WITH_AES_128_CBC_", new Integer(128));
    CIPHER_MAP.put("_WITH_IDEA_CBC_", new Integer(128));
    CIPHER_MAP.put("_WITH_DES_CBC_", new Integer(56));
    CIPHER_MAP.put("_WITH_RC2_CBC_40_", new Integer(40));
    CIPHER_MAP.put("_WITH_RC4_40_", new Integer(40));
    CIPHER_MAP.put("_WITH_DES40_CBC_", new Integer(40));
    CIPHER_MAP.put("_WITH_NULL_", new Integer(0));
  }
  private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
  private static final DebugTracer TRACER = getTracer();
@@ -124,12 +450,28 @@
  private final ByteChannelImpl pimpl = new ByteChannelImpl();
  private final ByteChannel channel;
  private final SSLEngine sslEngine;
  private ByteBuffer recvWrappedBuffer;
  private ByteBuffer recvUnwrappedBuffer;
  private ByteBuffer sendWrappedBuffer;
  private final Object handshakeLock = new Object();
  private final Object unwrapLock = new Object();
  private final Object wrapLock = new Object();
  private final Object readLock = new Object();
  private final Object writeLock = new Object();
  private TLSByteChannel(final LDAPConnectionHandlerCfg config,
      final ClientConnection c, final ByteChannel socketChannel,
      final ClientConnection c, final ByteChannel channel,
      final SSLContext sslContext)
  {
    this.socketChannel = socketChannel;
    this.channel = channel;
    // getHostName could potentially be very expensive and could block
    // the connection handler for several minutes. (See issue 4229)
@@ -137,11 +479,14 @@
    // avoid blocking new connections. Just remove for now to prevent
    // potential DoS attacks. SSL sessions will not be reused and some
    // cipher suites (such as Kerberos) will not work.
    // String hostName = socketChannel.socket().getInetAddress().getHostName();
    // int port = socketChannel.socket().getPort();
    // sslEngine = sslContext.createSSLEngine(hostName, port);
    sslEngine = sslContext.createSSLEngine();
    sslEngine.setUseClientMode(false);
    final Set<String> protocols = config.getSSLProtocol();
    if (!protocols.isEmpty())
    {
@@ -171,15 +516,18 @@
      break;
    }
    final SSLSession sslSession = sslEngine.getSession();
    sslBufferSize = sslSession.getPacketBufferSize();
    appBufSize = sslSession.getApplicationBufferSize();
    // Allocate read/write buffers.
    final SSLSession session = sslEngine.getSession();
    final int wrappedBufferSize = session.getPacketBufferSize();
    final int unwrappedBufferSize = session.getApplicationBufferSize();
    appNetData = ByteBuffer.allocate(sslBufferSize);
    netData = ByteBuffer.allocate(sslBufferSize);
    sendWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize);
    recvWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize);
    recvUnwrappedBuffer = ByteBuffer.allocate(unwrappedBufferSize);
    appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
    tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
    // Initially nothing has been received.
    recvWrappedBuffer.flip();
    recvUnwrappedBuffer.flip();
  }
@@ -187,27 +535,9 @@
  /**
   * {@inheritDoc}
   */
  public synchronized void close() throws IOException
  public ByteChannel getChannel()
  {
    sslEngine.closeInbound();
    sslEngine.closeOutbound();
    final SSLEngineResult.HandshakeStatus hsStatus = sslEngine
        .getHandshakeStatus();
    if (hsStatus != SSLEngineResult.HandshakeStatus.FINISHED
        && hsStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
    {
      doHandshakeWrite(hsStatus);
    }
  }
  /**
   * {@inheritDoc}
   */
  public int getAppBufSize()
  {
    return appBufSize;
    return pimpl;
  }
@@ -248,31 +578,15 @@
   */
  public int getSSF()
  {
    int cipherKeySSF = 0;
    final String cipherString = sslEngine.getSession().getCipherSuite();
    for (final Map.Entry<String, Integer> mapEntry : cipherMap.entrySet())
    for (final Map.Entry<String, Integer> mapEntry : CIPHER_MAP.entrySet())
    {
      if (cipherString.indexOf(mapEntry.getKey()) >= 0)
      {
        cipherKeySSF = mapEntry.getValue().intValue();
        break;
        return mapEntry.getValue().intValue();
      }
    }
    return cipherKeySSF;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isOpen()
  {
    if (sslEngine.isInboundDone() || sslEngine.isOutboundDone())
    {
      return false;
    }
    return true;
    return 0;
  }
@@ -285,269 +599,4 @@
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized int read(final ByteBuffer clearBuffer) throws IOException
  {
    SSLEngineResult.HandshakeStatus hsStatus;
    if (!reading)
    {
      appNetData.clear();
    }
    else
    {
      reading = false;
    }
    if (!socketChannel.isOpen())
    {
      return -1;
    }
    if (sslEngine.isInboundDone())
    {
      return -1;
    }
    do
    {
      final int wrappedBytes = socketChannel.read(appNetData);
      appNetData.flip();
      if (wrappedBytes == -1)
      {
        return -1;
      }
      hsStatus = sslEngine.getHandshakeStatus();
      if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
          || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
      {
        doHandshakeRead(hsStatus);
      }
      if (wrappedBytes == 0)
      {
        return 0;
      }
      while (appNetData.hasRemaining())
      {
        appData.clear();
        final SSLEngineResult res = sslEngine.unwrap(appNetData, appData);
        appData.flip();
        if (res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW)
        {
          appNetData.compact();
          reading = true;
          break;
        }
        else if (res.getStatus() != SSLEngineResult.Status.OK)
        {
          return -1;
        }
        hsStatus = sslEngine.getHandshakeStatus();
        if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
            || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
        {
          doHandshakeOp(hsStatus);
        }
        clearBuffer.put(appData);
      }
      hsStatus = sslEngine.getHandshakeStatus();
    }
    while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP);
    return clearBuffer.position();
  }
  /**
   * {@inheritDoc}
   */
  public ByteChannel wrapChannel(final ByteChannel channel)
  {
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized int write(final ByteBuffer clearData) throws IOException
  {
    if (!socketChannel.isOpen() || sslEngine.isOutboundDone())
    {
      throw new ClosedChannelException();
    }
    final int originalPosition = clearData.position();
    final int originalLimit = clearData.limit();
    final int length = originalLimit - originalPosition;
    if (length > sslBufferSize)
    {
      int pos = originalPosition;
      int lim = originalPosition + sslBufferSize;
      while (pos < originalLimit)
      {
        clearData.position(pos);
        clearData.limit(lim);
        writeInternal(clearData);
        pos = lim;
        lim = Math.min(originalLimit, pos + sslBufferSize);
      }
      return length;
    }
    else
    {
      return writeInternal(clearData);
    }
  }
  private void doHandshakeOp(SSLEngineResult.HandshakeStatus hsStatus)
      throws IOException
  {
    SSLEngineResult res;
    switch (hsStatus)
    {
    case NEED_TASK:
      hsStatus = doTasks();
      break;
    case NEED_WRAP:
      tempData.clear();
      netData.clear();
      res = sslEngine.wrap(tempData, netData);
      hsStatus = res.getHandshakeStatus();
      netData.flip();
      while (netData.hasRemaining())
      {
        socketChannel.write(netData);
      }
      hsStatus = sslEngine.getHandshakeStatus();
      return;
    default:
      return;
    }
  }
  private void doHandshakeRead(SSLEngineResult.HandshakeStatus hsStatus)
      throws IOException
  {
    do
    {
      doHandshakeOp(hsStatus);
      hsStatus = sslEngine.getHandshakeStatus();
    }
    while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK);
  }
  private void doHandshakeUnwrap() throws IOException
  {
    netData.clear();
    tempData.clear();
    final int bytesRead = socketChannel.read(netData);
    if (bytesRead <= 0)
    {
      throw new ClosedChannelException();
    }
    else
    {
      sslEngine.unwrap(netData, tempData);
    }
  }
  private void doHandshakeWrite(SSLEngineResult.HandshakeStatus hsStatus)
      throws IOException
  {
    do
    {
      if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
      {
        doHandshakeUnwrap();
      }
      else
      {
        doHandshakeOp(hsStatus);
      }
      hsStatus = sslEngine.getHandshakeStatus();
    }
    while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP);
  }
  private SSLEngineResult.HandshakeStatus doTasks()
  {
    Runnable task;
    while ((task = sslEngine.getDelegatedTask()) != null)
    {
      task.run();
    }
    return sslEngine.getHandshakeStatus();
  }
  private int writeInternal(final ByteBuffer clearData) throws IOException
  {
    int totBytesSent = 0;
    SSLEngineResult.HandshakeStatus hsStatus;
    hsStatus = sslEngine.getHandshakeStatus();
    if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
    {
      doHandshakeWrite(hsStatus);
    }
    while (clearData.hasRemaining())
    {
      netData.clear();
      final SSLEngineResult res = sslEngine.wrap(clearData, netData);
      netData.flip();
      if (netData.remaining() == 0)
      {
        // wrap didn't produce any data from our clear buffer.
        // Throw exception to prevent looping.
        throw new SSLException("SSLEngine.wrap produced 0 bytes");
      }
      if (res.getStatus() != SSLEngineResult.Status.OK)
      {
        throw new ClosedChannelException();
      }
      if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
          || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
          || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
      {
        doHandshakeWrite(hsStatus);
      }
      totBytesSent += socketChannel.write(netData);
    }
    return totBytesSent;
  }
}
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -23,7 +23,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2010-2011 ForgeRock AS.
 *      Portions Copyright 2010-2012 ForgeRock AS.
 */
package org.opends.server.protocols.ldap;
@@ -360,7 +360,7 @@
  private ASN1ByteChannelReader asn1Reader;
  private int APPLICATION_BUFFER_SIZE = 4096;
  private final int bufferSize;
  private final RedirectingByteChannel saslChannel;
  private final RedirectingByteChannel tlsChannel;
@@ -420,7 +420,7 @@
      this.useNanoTime=DirectoryServer.getUseNanoTime();
    }
    APPLICATION_BUFFER_SIZE = connectionHandler.getBufferSize();
    bufferSize = connectionHandler.getBufferSize();
    tlsChannel =
        RedirectingByteChannel.getRedirectingByteChannel(
@@ -428,7 +428,7 @@
    saslChannel =
        RedirectingByteChannel.getRedirectingByteChannel(tlsChannel);
    this.asn1Reader =
        ASN1.getReader(saslChannel, APPLICATION_BUFFER_SIZE, connectionHandler
        ASN1.getReader(saslChannel, bufferSize, connectionHandler
            .getMaxRequestSize());
    writeLock = new ReentrantLock();
@@ -939,16 +939,8 @@
    {
      if (asn1Writer == null)
      {
        if (isSecure())
        {
          int appBufSize = activeProvider.getAppBufSize();
          asn1Writer = ASN1.getWriter(saslChannel, writeLock, appBufSize);
        }
        else
        {
          asn1Writer = ASN1.getWriter(saslChannel, writeLock,
                  APPLICATION_BUFFER_SIZE);
        }
        asn1Writer = ASN1.getWriter(saslChannel, writeLock,
                  bufferSize);
        asn1WriterMap.put(currentThread, asn1Writer);
      }
@@ -2590,9 +2582,6 @@
   */
  public void enableTLS()
  {
    this.asn1Reader =
        ASN1.getReader(saslChannel, tlsPendingProvider.getAppBufSize(),
            connectionHandler.getMaxRequestSize());
    activeProvider = tlsPendingProvider;
    tlsChannel.redirect(tlsPendingProvider);
    tlsPendingProvider = null;
@@ -2608,9 +2597,6 @@
   */
  public void enableSSL(ConnectionSecurityProvider sslProvider)
  {
    this.asn1Reader =
        ASN1.getReader(saslChannel, sslProvider.getAppBufSize(),
            connectionHandler.getMaxRequestSize());
    activeProvider = sslProvider;
    tlsChannel.redirect(sslProvider);
  }
@@ -2624,10 +2610,6 @@
  {
    activeProvider = saslPendingProvider;
    saslChannel.redirect(saslPendingProvider);
    asn1Reader =
        ASN1.getReader(saslChannel,
            saslPendingProvider.getAppBufSize(), connectionHandler
                .getMaxRequestSize());
    saslPendingProvider = null;
  }
@@ -2657,7 +2639,7 @@
   * @return The TLS redirecting byte channel.
   */
   @Override
   public RedirectingByteChannel getChannel() {
   public ByteChannel getChannel() {
     return this.tlsChannel;
   }
@@ -2678,21 +2660,6 @@
  /**
   * Retrieves the application buffer size used in a LDAP client connection.
   * If a active security provider is being used, then the application buffer
   * size of that provider is returned.
   *
   * @return The application buffer size.
   */
  @Override
  public int getAppBufferSize() {
    if(activeProvider != null)
      return activeProvider.getAppBufSize();
    else
      return APPLICATION_BUFFER_SIZE;
  }
  /**
   * {@inheritDoc}
   */
  @Override