From 1dfff197eadcf24823d7915e6eead2a850f679f9 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 14 Feb 2012 16:09:28 +0000
Subject: [PATCH] Fix OPENDJ-420: Rare SSLExceptions while handling LDAPS connections and big LDAP searches
---
opends/src/server/org/opends/server/api/ClientConnection.java | 21
opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java | 18
opends/src/server/org/opends/server/extensions/SASLContext.java | 63 ++
opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java | 15
opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java | 14
opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java | 2
opends/src/server/org/opends/server/extensions/TLSByteChannel.java | 737 +++++++++++++++-------------
opends/src/server/org/opends/server/extensions/SASLByteChannel.java | 579 +++++++++-------------
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java | 47 -
9 files changed, 712 insertions(+), 784 deletions(-)
diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index 3fe256f..3ce44cd 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/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.
*
diff --git a/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java
index dbe19d4..287ea8b 100644
--- a/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java
+++ b/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();
}
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index 2c9a519..e459de7 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/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()) {
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
index 2c30316..213714d 100644
--- a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
+++ b/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);
diff --git a/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java b/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
index 134fdce..61fd118 100644
--- a/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
+++ b/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();
}
diff --git a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
index 03ab350..bf6ece9 100644
--- a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
+++ b/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);
- }
-
}
diff --git a/opends/src/server/org/opends/server/extensions/SASLContext.java b/opends/src/server/org/opends/server/extensions/SASLContext.java
index 78a6c5b..a549f70 100644
--- a/opends/src/server/org/opends/server/extensions/SASLContext.java
+++ b/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;
}
diff --git a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
index ad2b96e..9ddb118 100644
--- a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
+++ b/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;
- }
}
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index b67cc3a..3af5e14 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/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
--
Gitblit v1.10.0