From 88be99a38d4f02a6227ef5a2b514f77c6f28e524 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 08 Feb 2012 10:59:12 +0000
Subject: [PATCH] Preparation work for OPENDJ-420: Rare SSLExceptions while handling LDAPS connections and big LDAP searches
---
opends/src/server/org/opends/server/extensions/TLSByteChannel.java | 814 ++++++++++++++++++------------
opends/src/server/org/opends/server/extensions/SASLByteChannel.java | 708 +++++++++++++++-----------
2 files changed, 885 insertions(+), 637 deletions(-)
diff --git a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
index f7749d8..03ab350 100644
--- a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
@@ -28,354 +28,452 @@
package org.opends.server.extensions;
-import java.nio.channels.ByteChannel;
-import java.security.cert.Certificate;
+
+
import java.io.IOException;
import java.nio.ByteBuffer;
+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;
+
+
/**
* This class implements a SASL byte channel that can be used during
* confidentiality and integrity.
- *
*/
-public class
-SASLByteChannel implements ByteChannel, ConnectionSecurityProvider {
+public class SASLByteChannel implements ByteChannel, ConnectionSecurityProvider
+{
- // The client connection associated with this provider.
- private ClientConnection connection;
+ /**
+ * Return a SASL byte channel instance created using the specified parameters.
+ *
+ * @param c
+ * A client connection associated with the instance.
+ * @param name
+ * The name of the instance (SASL mechanism name).
+ * @param context
+ * A SASL context associated with the instance.
+ * @return A SASL byte channel.
+ */
+ public static SASLByteChannel getSASLByteChannel(final ClientConnection c,
+ final String name, final SASLContext context)
+ {
+ return new SASLByteChannel(c, name, context);
+ }
- // The SASL context associated with the provider
- private SASLContext saslContext;
- // The byte channel associated with this provider.
- private RedirectingByteChannel channel;
- // The number of bytes in the length buffer.
- private static final int lengthSize = 4;
+ // The SASL context associated with the provider
+ private SASLContext saslContext;
- //Length of the buffer.
- private int bufLength;
+ // The byte channel associated with this provider.
+ private final RedirectingByteChannel channel;
- // The SASL mechanism name.
- private String name;
+ // The number of bytes in the length buffer.
+ private static final int lengthSize = 4;
- //Buffers used in reading and decoding (unwrap)
- private ByteBuffer readBuffer, decodeBuffer;
+ // Length of the buffer.
+ private int bufLength;
- //How many bytes of the subsequent buffer is needed to complete a partially
- //read buffer.
- private int neededBytes = 0;
+ // The SASL mechanism name.
+ private final String name;
- //Used to not reset the buffer length size because the first 4 bytes of a
- //buffer are not size bytes.
- private boolean reading = false;
+ // Buffers used in reading and decoding (unwrap)
+ private final ByteBuffer readBuffer, decodeBuffer;
- /**
- * Create a SASL byte channel with the specified parameters
- * that is capable of processing a confidentiality/integrity SASL
- * connection.
- *
- * @param connection
- * The client connection to read/write the bytes.
- * @param name
- * The SASL mechanism name.
- * @param saslContext
- * The SASL context to process the data through.
- */
- private SASLByteChannel(ClientConnection connection, String name,
- SASLContext saslContext) {
- this.connection = connection;
- this.name = name;
- this.saslContext = saslContext;
- this.channel = connection.getChannel();
- this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize());
- this.decodeBuffer =
- ByteBuffer.allocate(connection.getAppBufferSize() + lengthSize);
+ // How many bytes of the subsequent buffer is needed to complete a partially
+ // read buffer.
+ private int neededBytes = 0;
+
+ // Used to not reset the buffer length size because the first 4 bytes of a
+ // buffer are not size bytes.
+ private boolean reading = false;
+
+
+
+ /**
+ * Create a SASL byte channel with the specified parameters that is capable of
+ * processing a confidentiality/integrity SASL connection.
+ *
+ * @param connection
+ * The client connection to read/write the bytes.
+ * @param name
+ * The SASL mechanism name.
+ * @param saslContext
+ * The SASL context to process the data through.
+ */
+ private SASLByteChannel(final ClientConnection connection, final String name,
+ final SASLContext saslContext)
+ {
+ this.name = name;
+ this.saslContext = saslContext;
+ this.channel = connection.getChannel();
+ this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize());
+ this.decodeBuffer = ByteBuffer.allocate(connection.getAppBufferSize()
+ + lengthSize);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void close() throws IOException
+ {
+ saslContext.dispose();
+ saslContext = null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getAppBufSize()
+ {
+ return saslContext.getBufSize(Sasl.MAX_BUFFER);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Certificate[] getClientCertificateChain()
+ {
+ return new Certificate[0];
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName()
+ {
+ return name;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getSSF()
+ {
+ return saslContext.getSSF();
+ }
+
+
+
+ /**
+ * {@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;
}
- /**
- * Return a SASL byte channel instance created using the specified
- * parameters.
- *
- * @param c A client connection associated with the instance.
- * @param name The name of the instance (SASL mechanism name).
- * @param context A SASL context associated with the instance.
- * @return A SASL byte channel.
- */
- public static SASLByteChannel
- getSASLByteChannel(ClientConnection c, String name,
- SASLContext context) {
- return new SASLByteChannel(c, name, context);
+ final int readResult = readAll(readBuffer, bytesToRead);
+ if (readResult == -1)
+ {
+ return -1;
}
- /**
- * Finish processing a previous, partially read buffer using some, or, all
- * of the bytes of the current buffer.
- *
- */
- private int processPartial(int readResult, ByteBuffer clearDst)
- throws IOException {
+ // 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();
- //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.
- byte[] inBytes = decodeBuffer.array();
- byte[]clearBytes = saslContext.unwrap(inBytes, lengthSize, bufLength);
- clearDst.put(clearBytes);
+ 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();
- 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;
+ clearDst.put(clearBytes);
+ readBuffer.clear();
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(ByteBuffer byteBuf, int total) throws IOException
- {
- while (channel.isOpen() && total > 0) {
- 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();
- }
- /**
- * 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(ByteBuffer byteBuf)
- {
- int answer = 0;
- for (int i = 0; i < lengthSize; i++)
- {
- byte b = byteBuf.get(i);
- answer <<= 8;
- answer |= ((int) b & 0xff);
- }
- return answer;
- }
-
- /**
- * {@inheritDoc}
- */
+ /**
+ * {@inheritDoc}
+ */
@Override
- public synchronized int read(ByteBuffer clearDst) throws IOException {
- int bytesToRead = lengthSize;
- if(reading)
- bytesToRead = neededBytes;
- 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);
+ 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);
}
- 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 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;
- } else {
- readBuffer.flip();
- decodeBuffer.put(readBuffer);
- byte[] inBytes = decodeBuffer.array();
- byte[]clearBytes = saslContext.unwrap(inBytes, lengthSize, bufLength);
- decodeBuffer.clear();
- clearDst.put(clearBytes);
- readBuffer.clear();
}
- return clearDst.position();
+ total -= count;
}
-
- /**
- * 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(byte[] buf, int len)
+ if (total > 0)
{
- for (int i = 3; i >= 0; i--)
- {
- buf[i] = (byte) (len & 0xff);
- len >>>= 8;
- }
+ return -1;
}
-
- /**
- * 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(byte[] clearBytes, int len) throws IOException {
- byte[] wrapBytes = saslContext.wrap(clearBytes, 0, len);
- byte[] outBytes = new byte[wrapBytes.length + lengthSize];
- writeBufLen(outBytes, wrapBytes.length);
- System.arraycopy(wrapBytes, 0, outBytes, lengthSize, wrapBytes.length);
- return ByteBuffer.wrap(outBytes);
+ else
+ {
+ return byteBuf.position();
}
+ }
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized int write(ByteBuffer clearSrc) throws IOException {
- int sendBufSize = getAppBufSize();
- int srcLen = clearSrc.remaining();
- ByteBuffer sendBuffer = ByteBuffer.allocate(sendBufSize);
- if (srcLen > sendBufSize) {
- 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));
- }
+
+ /**
+ * 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(ByteBuffer buffer) throws IOException {
- return channel.write(buffer);
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized void close() throws IOException {
- saslContext.dispose();
- saslContext=null;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isOpen() {
- return saslContext != null;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int getAppBufSize() {
- return saslContext.getBufSize(Sasl.MAX_BUFFER);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Certificate[] getClientCertificateChain() {
- return new Certificate[0];
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int getSSF() {
- return saslContext.getSSF();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ByteChannel wrapChannel(ByteChannel channel) {
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getName() {
- return name;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isSecure() {
- return true;
- }
+ /**
+ * 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/TLSByteChannel.java b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
index ff85170..ad2b96e 100644
--- a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
@@ -23,9 +23,12 @@
*
*
* Copyright 2008-2009 Sun Microsystems, Inc.
+ * Portions copyright 2012 ForgeRock AS.
*/
package org.opends.server.extensions;
+
+
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
@@ -45,359 +48,506 @@
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DebugLogLevel;
+
+
/**
* A class that provides a TLS byte channel implementation.
- *
*/
-public class TLSByteChannel implements
- ByteChannel, ConnectionSecurityProvider {
+public class TLSByteChannel implements ByteChannel, ConnectionSecurityProvider
+{
+ private static final DebugTracer TRACER = getTracer();
- private static final DebugTracer TRACER = getTracer();
+ private final ByteChannel socketChannel;
- private final ClientConnection connection;
- private final ByteChannel socketChannel;
+ private final SSLEngine sslEngine;
- private final SSLEngine sslEngine;
+ // read copy to buffer
+ private final ByteBuffer appData;
- //read copy to buffer
- private final ByteBuffer appData;
- //read encrypted
- private final ByteBuffer appNetData;
+ // read encrypted
+ private final ByteBuffer appNetData;
- //Write encrypted
- private final ByteBuffer netData, tempData;
- private final int sslBufferSize, appBufSize;
- private boolean reading = false;
+ // Write encrypted
+ private final ByteBuffer netData;
+ private final ByteBuffer tempData;
- //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 final int sslBufferSize;
+ private final int appBufSize;
- 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));
- };
+ private boolean reading = false;
- private TLSByteChannel(LDAPConnectionHandlerCfg config, ClientConnection c,
- ByteChannel socketChannel, SSLContext sslContext) {
+ // 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;
+ 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));
+ };
- this.socketChannel = socketChannel;
- this.connection = c;
- // getHostName could potentially be very expensive and could block
- // the connection handler for several minutes. (See issue 4229)
- // Accepting new connections should be done in a seperate thread to
- // 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);
- Set<String> protocols = config.getSSLProtocol();
- if (!protocols.isEmpty())
- sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
- Set<String> ciphers = config.getSSLCipherSuite();
- if (!ciphers.isEmpty())
- sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
- switch (config.getSSLClientAuthPolicy()) {
- case DISABLED:
- sslEngine.setNeedClientAuth(false);
- sslEngine.setWantClientAuth(false);
- break;
- case REQUIRED:
- sslEngine.setWantClientAuth(true);
- sslEngine.setNeedClientAuth(true);
- break;
- case OPTIONAL:
- default:
- sslEngine.setNeedClientAuth(false);
- sslEngine.setWantClientAuth(true);
- break;
+
+
+ /**
+ * Create an TLS byte channel instance using the specified LDAP connection
+ * configuration, client connection, SSL context and socket channel
+ * parameters.
+ *
+ * @param config
+ * The LDAP connection configuration.
+ * @param c
+ * The client connection.
+ * @param sslContext
+ * The SSL context.
+ * @param socketChannel
+ * The socket channel.
+ * @return A TLS capable byte channel.
+ */
+ public static TLSByteChannel getTLSByteChannel(
+ final LDAPConnectionHandlerCfg config, final ClientConnection c,
+ final SSLContext sslContext, final ByteChannel socketChannel)
+ {
+ return new TLSByteChannel(config, c, socketChannel, sslContext);
+ }
+
+
+
+ private TLSByteChannel(final LDAPConnectionHandlerCfg config,
+ final ClientConnection c, final ByteChannel socketChannel,
+ final SSLContext sslContext)
+ {
+
+ this.socketChannel = socketChannel;
+
+ // getHostName could potentially be very expensive and could block
+ // the connection handler for several minutes. (See issue 4229)
+ // Accepting new connections should be done in a seperate thread to
+ // 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())
+ {
+ sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
+ }
+
+ final Set<String> ciphers = config.getSSLCipherSuite();
+ if (!ciphers.isEmpty())
+ {
+ sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
+ }
+
+ switch (config.getSSLClientAuthPolicy())
+ {
+ case DISABLED:
+ sslEngine.setNeedClientAuth(false);
+ sslEngine.setWantClientAuth(false);
+ break;
+ case REQUIRED:
+ sslEngine.setWantClientAuth(true);
+ sslEngine.setNeedClientAuth(true);
+ break;
+ case OPTIONAL:
+ default:
+ sslEngine.setNeedClientAuth(false);
+ sslEngine.setWantClientAuth(true);
+ break;
+ }
+
+ final SSLSession sslSession = sslEngine.getSession();
+ sslBufferSize = sslSession.getPacketBufferSize();
+ appBufSize = sslSession.getApplicationBufferSize();
+
+ appNetData = ByteBuffer.allocate(sslBufferSize);
+ netData = ByteBuffer.allocate(sslBufferSize);
+
+ appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
+ tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized void close() throws IOException
+ {
+ 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;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Certificate[] getClientCertificateChain()
+ {
+ try
+ {
+ return sslEngine.getSession().getPeerCertificates();
+ }
+ catch (final SSLPeerUnverifiedException e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ return new Certificate[0];
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName()
+ {
+ return "TLS";
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getSSF()
+ {
+ int cipherKeySSF = 0;
+ final String cipherString = sslEngine.getSession().getCipherSuite();
+ for (final Map.Entry<String, Integer> mapEntry : cipherMap.entrySet())
+ {
+ if (cipherString.indexOf(mapEntry.getKey()) >= 0)
+ {
+ cipherKeySSF = mapEntry.getValue().intValue();
+ break;
+ }
+ }
+ return cipherKeySSF;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isOpen()
+ {
+ if (sslEngine.isInboundDone() || sslEngine.isOutboundDone())
+ {
+ return false;
+ }
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isSecure()
+ {
+ 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;
}
- SSLSession sslSession = sslEngine.getSession();
- sslBufferSize = sslSession.getPacketBufferSize();
- appBufSize = sslSession.getApplicationBufferSize();
-
- appNetData = ByteBuffer.allocate(sslBufferSize);
- netData = ByteBuffer.allocate(sslBufferSize);
-
- appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
- tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
- }
-
- /**
- * {@inheritDoc}
- */
- public int getAppBufSize() {
- return appBufSize;
- }
-
- /**
- * Create an TLS byte channel instance using the specified LDAP connection
- * configuration, client connection, SSL context and socket channel
- * parameters.
- *
- * @param config The LDAP connection configuration.
- * @param c The client connection.
- * @param sslContext The SSL context.
- * @param socketChannel The socket channel.
- * @return A TLS capable byte channel.
- */
- public static TLSByteChannel
- getTLSByteChannel(LDAPConnectionHandlerCfg config, ClientConnection c,
- SSLContext sslContext, ByteChannel socketChannel) {
- return new TLSByteChannel(config, c, socketChannel, sslContext);
- }
-
- private SSLEngineResult.HandshakeStatus doTasks() {
- Runnable task;
- while ((task = sslEngine.getDelegatedTask()) != null)
- task.run();
- return sslEngine.getHandshakeStatus();
- }
-
- 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 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;
+ else if (res.getStatus() != SSLEngineResult.Status.OK)
+ {
+ return -1;
}
- }
- /**
- * {@inheritDoc}
- */
- public synchronized int read(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 {
- 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();
- 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 synchronized void close() throws IOException {
- sslEngine.closeInbound();
- sslEngine.closeOutbound();
- SSLEngineResult.HandshakeStatus hsStatus =
- sslEngine.getHandshakeStatus();
- if(hsStatus != SSLEngineResult.HandshakeStatus.FINISHED &&
- hsStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
- doHandshakeWrite(hsStatus);
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isOpen() {
- if(sslEngine.isInboundDone() || sslEngine.isOutboundDone())
- return false;
- return true;
- }
-
- /**
- * {@inheritDoc}
- */
- public int getSSF() {
- int cipherKeySSF = 0;
- String cipherString = sslEngine.getSession().getCipherSuite();
- for(Map.Entry<String, Integer> mapEntry : cipherMap.entrySet()) {
- if(cipherString.indexOf(mapEntry.getKey()) >= 0) {
- cipherKeySSF = mapEntry.getValue().intValue();
- break;
- }
- }
- return cipherKeySSF;
- }
-
- /**
- * {@inheritDoc}
- */
- public Certificate[] getClientCertificateChain() {
- try {
- return sslEngine.getSession().getPeerCertificates();
- }
- catch (SSLPeerUnverifiedException e) {
- if (debugEnabled()) {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- return new Certificate[0];
- }
- }
-
- private void doHandshakeUnwrap() throws IOException {
- netData.clear();
- tempData.clear();
- 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);
- }
-
- /**
- * {@inheritDoc}
- */
- public synchronized int write(ByteBuffer clearData) throws IOException {
- if(!socketChannel.isOpen() || sslEngine.isOutboundDone()) {
- throw new ClosedChannelException();
- }
- int originalPosition = clearData.position();
- int originalLimit = clearData.limit();
- 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 int writeInternal(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();
- 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);
+ if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
+ || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
+ {
+ doHandshakeOp(hsStatus);
}
- return totBytesSent;
+ 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();
}
- /**
- * {@inheritDoc}
- */
- public ByteChannel wrapChannel(ByteChannel channel) {
- return this;
+ 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);
}
- /**
- * {@inheritDoc}
- */
- public String getName() {
- return "TLS";
- }
+ 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");
+ }
- /**
- * {@inheritDoc}
- */
- public boolean isSecure() {
- return true;
+ 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;
+ }
}
--
Gitblit v1.10.0