opends/src/server/org/opends/server/api/ClientConnection.java
@@ -31,6 +31,7 @@ import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -45,6 +46,7 @@ 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; @@ -1390,6 +1392,50 @@ } /** * Return the lowest level channel associated with a connection. * This is normally the channel associated with the socket * channel. * * @return The lowest level channel associated with a connection. */ public RedirectingByteChannel getChannel() { // By default, return null, which indicates that there should // be no channel. Subclasses should override this if // they want to support a channel. return null; } /** * Return the Socket channel associated with a connection. * * @return The Socket channel associated with a connection. */ public SocketChannel getSocketChannel() { // By default, return null, which indicates that there should // be no socket channel. Subclasses should override this if // they want to support a socket channel. return null; } /** * 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 opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Copyright 2006-2009 Sun Microsystems, Inc. */ package org.opends.server.extensions; @@ -163,6 +163,17 @@ (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, SASL_MECHANISM_DIGEST_MD5, identityMapper); } catch (SaslException ex) { opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -383,8 +383,19 @@ { try { saslContext = SASLContext.createSASLContext(saslProps, serverFQDN, SASL_MECHANISM_GSSAPI, identityMapper); //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_GSSAPI, identityMapper); } else { saslContext = SASLContext.createSASLContext(saslProps, serverFQDN, SASL_MECHANISM_GSSAPI, identityMapper); } } catch (SaslException ex) { opends/src/server/org/opends/server/extensions/SASLByteChannel.java
@@ -22,22 +22,18 @@ * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Copyright 2008-2009 Sun Microsystems, Inc. */ package org.opends.server.extensions; import java.nio.channels.ByteChannel; import java.security.cert.Certificate; import static org.opends.server.loggers.debug.DebugLogger.*; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SocketChannel; import javax.security.sasl.Sasl; import org.opends.server.api.ClientConnection; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.protocols.ldap.LDAPClientConnection; import org.opends.server.util.StaticUtils; /** @@ -48,27 +44,34 @@ public class SASLByteChannel implements ByteChannel, ConnectionSecurityProvider { // The tracer object for the debug logger. private static final DebugTracer TRACER = getTracer(); // The client connection associated with this provider. private ClientConnection connection; // The socket channel associated with this provider. private SocketChannel sockChannel; // 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 final int lengthSize = 4; // A byte buffer used to hold the length of the clear buffer. private ByteBuffer lengthBuf = ByteBuffer.allocate(lengthSize); //Length of the buffer. private int bufLength; // The SASL mechanism name. private String name; //Buffers used in reading and decoding (unwrap) private ByteBuffer readBuffer, decodeBuffer; //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 @@ -87,7 +90,9 @@ this.connection = connection; this.name = name; this.saslContext = saslContext; this.sockChannel = ((LDAPClientConnection) connection).getSocketChannel(); this.channel = connection.getChannel(); this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize()); this.decodeBuffer = ByteBuffer.allocate(connection.getAppBufferSize()); } /** @@ -96,7 +101,7 @@ * * @param c A client connection associated with the instance. * @param name The name of the instance (SASL mechanism name). * @param context A SASL context associaetd with the instance. * @param context A SASL context associated with the instance. * @return A SASL byte channel. */ public static SASLByteChannel @@ -106,8 +111,44 @@ } /** * Read from the socket channel into the specified byte buffer the * number of bytes specified in the total parameter. * 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 { 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.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. @@ -121,8 +162,8 @@ private int readAll(ByteBuffer byteBuf, int total) throws IOException { int count = 0; while (sockChannel.isOpen() && total > 0) { count = sockChannel.read(byteBuf); while (channel.isOpen() && total > 0) { count = channel.read(byteBuf); if (count == -1) return -1; if (count == 0) return 0; total -= count; @@ -144,43 +185,53 @@ private int getBufLength(ByteBuffer byteBuf) { int answer = 0; byte[] buf = byteBuf.array(); for (int i = 0; i < lengthSize; i++) { byte b = byteBuf.get(i); answer <<= 8; answer |= ((int) buf[i] & 0xff); answer |= ((int) b & 0xff); } return answer; } /** * {@inheritDoc} */ public int read(ByteBuffer clearDst) throws IOException { int recvBufSize = getAppBufSize(); if(recvBufSize > clearDst.capacity()) return -1; lengthBuf.clear(); int readResult = readAll(lengthBuf, lengthSize); if (readResult == -1) return -1; else if (readResult == 0) return 0; int bufLength = getBufLength(lengthBuf); if (bufLength > recvBufSize) //TODO SASLPhase2 add message return -1; ByteBuffer readBuf = ByteBuffer.allocate(bufLength); readResult = readAll(readBuf, bufLength); if (readResult == -1) return -1; else if (readResult == 0) return 0; byte[] inBytes = readBuf.array(); byte[] clearBytes = saslContext.unwrap(inBytes, 0, inBytes.length); for(int i = 0; i < clearBytes.length; i++) { clearDst.put(clearBytes[i]); } return clearDst.remaining(); 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); } 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() + 4; readBuffer.flip(); decodeBuffer.put(readBuffer); readBuffer.clear(); 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(); } /** @@ -258,12 +309,11 @@ * to the socket channel, or, {@code false} if not. */ private int writeChannel(ByteBuffer buffer) throws IOException { int bytesWritten = sockChannel.write(buffer); int bytesWritten = channel.write(buffer); if (bytesWritten < 0) throw new ClosedChannelException(); else if (bytesWritten == 0) { if(!StaticUtils.writeWithTimeout( connection, sockChannel, buffer)) if(!StaticUtils.writeWithTimeout(connection, buffer)) throw new ClosedChannelException(); } return bytesWritten; @@ -288,7 +338,7 @@ * {@inheritDoc} */ public int getAppBufSize() { return saslContext.getBufSize(Sasl.RAW_SEND_SIZE) + lengthSize; return saslContext.getBufSize(Sasl.MAX_BUFFER); } /** opends/src/server/org/opends/server/extensions/TLSByteChannel.java
@@ -205,6 +205,8 @@ SSLEngineResult.HandshakeStatus hsStatus; if(!reading) appNetData.clear(); else reading = false; if(!socketChannel.isOpen()) return -1; if(sslEngine.isInboundDone()) @@ -369,8 +371,7 @@ throw new ClosedChannelException(); else if (bytesWritten == 0) { int bytesSent = netData.remaining(); if(!StaticUtils.writeWithTimeout( connection, socketChannel, netData)) if(!StaticUtils.writeWithTimeout(connection, netData)) throw new ClosedChannelException(); totBytesSent += bytesSent; } else opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -195,6 +195,8 @@ private ASN1ByteChannelReader asn1Reader; private static int APPLICATION_BUFFER_SIZE = 4096; private final RedirectingByteChannel saslChannel; private final RedirectingByteChannel tlsChannel; private ConnectionSecurityProvider activeProvider = null; @@ -286,7 +288,7 @@ saslChannel = RedirectingByteChannel.getRedirectingByteChannel(tlsChannel); this.asn1Reader = ASN1.getReader(saslChannel, 4096, connectionHandler ASN1.getReader(saslChannel, APPLICATION_BUFFER_SIZE, connectionHandler .getMaxRequestSize()); connectionID = DirectoryServer.newConnectionAccepted(this); @@ -349,6 +351,7 @@ * @return The socket channel that can be used to communicate with the * client. */ @Override public SocketChannel getSocketChannel() { return clientChannel; @@ -775,7 +778,8 @@ writerBuffer.writer = ASN1.getWriter(saslChannel, appBufSize); } else writerBuffer.writer = ASN1.getWriter(saslChannel, 4096); writerBuffer.writer = ASN1.getWriter(saslChannel, APPLICATION_BUFFER_SIZE); cachedBuffers.set(writerBuffer); } try @@ -795,7 +799,7 @@ if (keepStats) { // TODO SASLPhase2 hard-coded for now, flush probably needs to // TODO hard-coded for now, flush probably needs to // return how many bytes were flushed. statTracker.updateMessageWritten(message, 4096); } @@ -2552,6 +2556,19 @@ /** * Retrieves the TLS redirecting byte channel used in a LDAP client * connection. * * @return The TLS redirecting byte channel. */ @Override public RedirectingByteChannel getChannel() { return this.tlsChannel; } /** * {@inheritDoc} */ @Override @@ -2565,6 +2582,23 @@ /** * 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; } private void initializeOperationMonitors() { this.addMonitor = OperationMonitor.getOperationMonitor(ADD); opends/src/server/org/opends/server/util/StaticUtils.java
@@ -4170,21 +4170,14 @@ * problem). If possible, it will attempt to use the selector returned * by the {@code ClientConnection.getWriteSelector} method, but it is * capable of working even if that method returns {@code null}. <BR> * <BR> * Note that this method has been written in a generic manner so that * other connection security providers can use it to send data to the * client, provided that the given buffer contains the appropriate * pre-encoded information. <BR> * <BR> * Also note that the original position and limit values will not be * * Note that the original position and limit values will not be * preserved, so if that is important to the caller, then it should * record them before calling this method and restore them after it * returns. * * @param clientConnection * The client connection to which the data is to be written. * @param socketChannel * The socket channel over which to write the data. * @param buffer * The data to be written to the client. * @return <CODE>true</CODE> if all the data in the provided buffer was @@ -4198,10 +4191,10 @@ * client. The caller will be responsible for catching this * and terminating the client connection. */ public static boolean writeWithTimeout( ClientConnection clientConnection, SocketChannel socketChannel, public static boolean writeWithTimeout(ClientConnection clientConnection, ByteBuffer buffer) throws IOException { SocketChannel socketChannel = clientConnection.getSocketChannel(); long startTime = System.currentTimeMillis(); long waitTime = clientConnection.getMaxBlockedWriteTimeLimit(); if (waitTime <= 0)