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

dugan
18.19.2009 b45e7fb00a64d2fd8897a485def4296d03c39b55
Commit SASL Phase2 changes. Issue 3805. Unit tests to follow later.
7 files modified
270 ■■■■ changed files
opends/src/server/org/opends/server/api/ClientConnection.java 46 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java 13 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/SASLByteChannel.java 140 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/TLSByteChannel.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java 40 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 15 ●●●● patch | view | raw | blame | history
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,9 +383,20 @@
    {
      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_GSSAPI, identityMapper);
        } else {
        saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
            SASL_MECHANISM_GSSAPI, identityMapper);
      }
      }
      catch (SaslException ex)
      {
        if (debugEnabled())
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);
      int bytesToRead = lengthSize;
      if(reading)
        bytesToRead = neededBytes;
      int readResult = readAll(readBuffer, bytesToRead);
        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]);
      //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);
        }
        return clearDst.remaining();
      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)